JQuery in, Prototype/Scriptaculous out (#11445).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10068 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2012-07-22 13:29:26 +00:00
parent 387836f8aa
commit 6a2ca5e034
122 changed files with 1431 additions and 10962 deletions

View File

@ -1,7 +1,7 @@
source 'http://rubygems.org'
gem 'rails', '3.2.6'
gem 'prototype-rails', '3.2.1'
gem "jquery-rails", "~> 2.0.2"
gem "i18n", "~> 0.6.0"
gem "coderay", "~> 1.0.6"
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]

View File

@ -20,7 +20,7 @@ class AutoCompletesController < ApplicationController
def issues
@issues = []
q = params[:q].to_s
q = (params[:q] || params[:term]).to_s
query = (params[:scope] == "all" && Setting.cross_project_issue_relations?) ? Issue : @project.issues
if q.match(/^\d+$/)
@issues << query.visible.find_by_id(q.to_i)

View File

@ -179,7 +179,8 @@ class MyController < ApplicationController
group = params[:group]
@user = User.current
if group.is_a?(String)
group_items = (params["list-#{group}"] || []).collect(&:underscore)
group_items = (params["blocks"] || []).collect(&:underscore)
group_items.each {|s| s.sub!(/^block_/, '')}
if group_items and group_items.is_a? Array
layout = @user.pref[:my_page_layout] || {}
# remove group blocks if they are presents in other groups

View File

@ -42,7 +42,6 @@ class RepositoriesController < ApplicationController
@repository = Repository.factory(scm)
@repository.is_default = @project.repository.nil?
@repository.project = @project
render :layout => !request.xhr?
end
def create

View File

@ -43,13 +43,6 @@ module ApplicationHelper
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
end
# Display a link to remote if user is authorized
def link_to_remote_if_authorized(name, options = {}, html_options = nil)
ActiveSupport::Deprecation.warn "ApplicationHelper#link_to_remote_if_authorized is deprecated and will be removed in Redmine 2.2."
url = options[:url] || {}
link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
end
# Displays a link to user's account page if active
def link_to_user(user, options={})
if user.is_a?(User)
@ -161,8 +154,8 @@ module ApplicationHelper
end
def toggle_link(name, id, options={})
onclick = "Element.toggle('#{id}'); "
onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
onclick = "$('##{id}').toggle(); "
onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
onclick << "return false;"
link_to(name, "#", :onclick => onclick)
end
@ -175,11 +168,6 @@ module ApplicationHelper
}))
end
def prompt_to_remote(name, text, param, url, html_options = {})
html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
link_to name, {}, html_options
end
def format_activity_title(text)
h(truncate_single_line(text, :length => 100))
end
@ -1042,7 +1030,7 @@ module ApplicationHelper
end
@context_menu_included = true
end
javascript_tag "new ContextMenu('#{ url_for(url) }')"
javascript_tag "contextMenuInit('#{ url_for(url) }')"
end
def calendar_for(field_id)
@ -1173,9 +1161,9 @@ module ApplicationHelper
# Returns the javascript tags that are included in the html layout head
def javascript_heads
tags = javascript_include_tag('prototype', 'effects', 'dragdrop', 'controls', 'rails', 'application')
tags = javascript_include_tag('jquery-1.7.2-ui-1.8.21-ujs-2.0.2', 'application')
unless User.current.pref.warn_on_leaving_unsaved == '0'
tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
end
tags
end

View File

@ -29,7 +29,7 @@ module JournalsHelper
:method => 'post',
:title => l(:button_quote)) if options[:reply_links]
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal },
{ :controller => 'journals', :action => 'edit', :id => journal, :format => 'js' },
:title => l(:button_edit)) if editable
end
content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
@ -40,7 +40,7 @@ module JournalsHelper
end
def link_to_in_place_notes_editor(text, field_id, url, options={})
onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
onclick = "$.ajax({url: '#{url_for(url)}', type: 'get'}); return false;"
link_to text, '#', options.merge(:onclick => onclick)
end
end

View File

@ -141,12 +141,7 @@ module RepositoriesHelper
select_tag('repository_scm',
options_for_select(scm_options, repository.class.name.demodulize),
:disabled => (repository && !repository.new_record?),
:onchange => remote_function(
:url => new_project_repository_path(@project),
:method => :get,
:update => 'content',
:with => "Form.serialize(this.form)")
)
:data => {:remote => true, :method => 'get'})
end
def with_leading_slash(path)

View File

@ -36,7 +36,7 @@
</td>
</tr>
</table>
<%= javascript_tag "Form.Element.focus('username');" %>
<%= javascript_tag "$('#username').focus();" %>
<% end %>
</div>
<%= call_hook :view_account_login_bottom %>

View File

@ -1,9 +1,7 @@
<ul>
<% if @issues.any? -%>
<% @issues.each do |issue| -%>
<%= content_tag 'li', h("#{issue.tracker} ##{issue.id}: #{issue.subject}"), :id => issue.id %>
<% end -%>
<% else -%>
<%= content_tag("li", l(:label_none), :style => 'display:none') %>
<% end -%>
</ul>
<%= raw @issues.map {|issue| {
'id' => issue.id,
'label' => "#{issue.tracker} ##{issue.id}: #{truncate issue.subject.to_s, :length => 60}",
'value' => issue.id
}
}.to_json
%>

View File

@ -4,7 +4,7 @@
<%= link_to_if_authorized l(:label_message_new),
{:controller => 'messages', :action => 'new', :board_id => @board},
:class => 'icon icon-add',
:onclick => 'Element.show("add-message"); Form.Element.focus("message_subject"); return false;' %>
:onclick => 'showAndScrollTo("add-message", "message_subject"); return false;' %>
<%= watcher_tag(@board, User.current) %>
</div>
@ -15,7 +15,7 @@
<%= render :partial => 'messages/form', :locals => {:f => f} %>
<p><%= submit_tag l(:button_create) %>
<%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board}, 'message-form') %> |
<%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-message")' %></p>
<%= link_to l(:button_cancel), "#", :onclick => '$("#add-message").hide(); return false;' %></p>
<% end %>
<div id="preview" class="wiki"></div>
<% end %>

View File

@ -20,7 +20,7 @@
<%= label_tag('year', l(:label_year)) %>
<%= select_year(@year, :prefix => "year", :discard_type => true) %>
<%= link_to_function l(:button_apply), '$("query_form").submit()', :class => 'icon icon-checked' %>
<%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %>
<%= link_to l(:button_clear), { :project_id => @project, :set_filter => 1 }, :class => 'icon icon-reload' %>
</p>
<% end %>

View File

@ -16,8 +16,8 @@
</div>
<script>
Event.observe(window, 'load', function() { displayTabsButtons(); });
Event.observe(window, 'resize', function() { displayTabsButtons(); });
$(document).ready(displayTabsButtons);
$(window).resize(displayTabsButtons);
</script>
<% tabs.each do |tab| -%>

View File

@ -3,63 +3,65 @@
<script type="text/javascript">
//<![CDATA[
function toggle_custom_field_format() {
format = $("custom_field_field_format");
p_length = $("custom_field_min_length");
p_regexp = $("custom_field_regexp");
p_values = $("custom_field_possible_values");
p_searchable = $("custom_field_searchable");
p_default = $("custom_field_default_value");
p_multiple = $("custom_field_multiple");
var format = $("#custom_field_field_format").val();
var p_length = $("#custom_field_min_length");
var p_regexp = $("#custom_field_regexp");
var p_values = $("#custom_field_possible_values");
var p_searchable = $("#custom_field_searchable");
var p_default = $("#custom_field_default_value");
var p_multiple = $("#custom_field_multiple");
// can't change type on JQuery objects
var p_default2 = document.getElementById("custom_field_default_value");
p_default.setAttribute('type','text');
Element.show(p_default.parentNode);
p_default2.type = 'text';
p_default.parent().show();
switch (format.value) {
switch (format) {
case "list":
Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode);
if (p_searchable) Element.show(p_searchable.parentNode);
Element.show(p_values.parentNode);
Element.show(p_multiple.parentNode);
p_length.parent().hide();
p_regexp.parent().hide();
p_searchable.parent().show();
p_values.parent().show();
p_multiple.parent().show();
break;
case "bool":
p_default.setAttribute('type','checkbox');
Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode);
if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_multiple.parentNode);
p_default2.type = 'checkbox';
p_length.parent().hide();
p_regexp.parent().hide();
p_searchable.parent().hide();
p_values.parent().hide();
p_multiple.parent().hide();
break;
case "date":
Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode);
if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_multiple.parentNode);
p_length.parent().hide();
p_regexp.parent().hide();
p_searchable.parent().hide();
p_values.parent().hide();
p_multiple.parent().hide();
break;
case "float":
case "int":
Element.show(p_length.parentNode);
Element.show(p_regexp.parentNode);
if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_multiple.parentNode);
p_length.parent().show();
p_regexp.parent().show();
p_searchable.parent().hide();
p_values.parent().hide();
p_multiple.parent().hide();
break;
case "user":
case "version":
Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode);
if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_default.parentNode);
Element.show(p_multiple.parentNode);
p_length.parent().hide();
p_regexp.parent().hide();
p_searchable.parent().hide();
p_values.parent().hide();
p_multiple.parent().show();
p_default.parent().hide();
break;
default:
Element.show(p_length.parentNode);
Element.show(p_regexp.parentNode);
if (p_searchable) Element.show(p_searchable.parentNode);
Element.hide(p_values.parentNode);
Element.hide(p_multiple.parentNode);
p_length.parent().show();
p_regexp.parent().show();
p_searchable.parent().show();
p_values.parent().hide();
p_multiple.parent().hide();
break;
}
}

View File

@ -1,6 +1,6 @@
<div class="contextual">
<%= link_to l(:label_document_new), new_project_document_path(@project), :class => 'icon icon-add',
:onclick => 'Element.show("add-document"); Form.Element.focus("document_title"); return false;' if User.current.allowed_to?(:manage_documents, @project) %>
:onclick => 'showAndScrollTo("add-document", "document_title"); return false;' if User.current.allowed_to?(:manage_documents, @project) %>
</div>
<div id="add-document" style="display:none;">
@ -9,7 +9,7 @@
<%= render :partial => 'form', :locals => {:f => f} %>
<p>
<%= submit_tag l(:button_create) %>
<%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-document")' %>
<%= link_to l(:button_cancel), "#", :onclick => '$("#add-document").hide(); return false;' %>
</p>
<% end %>
</div>

View File

@ -17,7 +17,7 @@
<%= link_to_attachments @document %>
<% if authorize_for('documents', 'add_attachment') %>
<p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
<p><%= link_to l(:label_attachment_new), {}, :onclick => "$('#add_attachment_form').show(); return false;",
:id => 'attach_files_link' %></p>
<%= form_tag({ :controller => 'documents', :action => 'add_attachment', :id => @document }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %>
<div class="box">

View File

@ -25,14 +25,14 @@
<p><%= submit_tag l(:button_change) %>
<%= link_to_function(
l(:button_cancel),
"$('member-#{membership.id}-roles').show(); $('member-#{membership.id}-roles-form').hide(); return false;"
"$('#member-#{membership.id}-roles').show(); $('#member-#{membership.id}-roles-form').hide(); return false;"
) %></p>
<% end %>
</td>
<td class="buttons">
<%= link_to_function(
l(:button_edit),
"$('member-#{membership.id}-roles').hide(); $('member-#{membership.id}-roles-form').show(); return false;",
"$('#member-#{membership.id}-roles').hide(); $('#member-#{membership.id}-roles-form').show(); return false;",
:class => 'icon icon-edit'
) %>
<%= delete_link({:controller => 'groups', :action => 'destroy_membership', :id => @group, :membership_id => membership},

View File

@ -29,15 +29,7 @@
<fieldset><legend><%=l(:label_user_new)%></legend>
<p><%= label_tag "user_search", l(:label_user_search) %><%= text_field_tag 'user_search', nil %></p>
<%= observe_field(:user_search,
:frequency => 0.5,
:update => :users,
:url => autocomplete_for_user_group_path(@group),
:method => :get,
:before => '$("user_search").addClassName("ajax-loading")',
:complete => '$("user_search").removeClassName("ajax-loading")',
:with => 'q')
%>
<%= javascript_tag "observeSearchfield('user_search', 'users', '#{ escape_javascript autocomplete_for_user_group_path(@group) }')" %>
<div id="users">
<%= principals_check_box_tags 'user_ids[]', users %>

View File

@ -1,4 +1,4 @@
Element.update('tab-content-users', '<%= escape_javascript(render :partial => 'groups/users') %>');
$('#tab-content-users').html('<%= escape_javascript(render :partial => 'groups/users') %>');
<% @users.each do |user| %>
new Effect.Highlight('user-<%= user.id %>');
$('#user-<%= user.id %>').effect("highlight");
<% end %>

View File

@ -1 +1 @@
Element.update('tab-content-memberships', '<%= escape_javascript(render :partial => 'groups/memberships') %>');
$('#tab-content-memberships').html('<%= escape_javascript(render :partial => 'groups/memberships') %>');

View File

@ -1,6 +1,6 @@
<% if @membership.valid? %>
Element.update('tab-content-memberships', '<%= escape_javascript(render :partial => 'groups/memberships') %>');
new Effect.Highlight('member-<%= @membership.id %>');
$('#tab-content-memberships').html('<%= escape_javascript(render :partial => 'groups/memberships') %>');
$('#member-<%= @membership.id %>').effect("highlight");
<% else %>
alert('<%= escape_javascript(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', '))) %>');
<% end %>

View File

@ -1 +1 @@
Element.update('tab-content-users', '<%= escape_javascript(render :partial => 'groups/users') %>');
$('#tab-content-users').html('<%= escape_javascript(render :partial => 'groups/users') %>');

View File

@ -1,3 +1,3 @@
hideModal();
<% select = content_tag('select', content_tag('option') + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]') %>
Element.replace('issue_category_id', '<%= escape_javascript(select) %>');
$('#issue_category_id').replaceWith('<%= escape_javascript(select) %>');

View File

@ -1,3 +1,2 @@
Element.update('ajax-modal', '<%= escape_javascript(render :partial => 'issue_categories/new_modal') %>');
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'issue_categories/new_modal') %>');
showModal('ajax-modal', '600px');
Form.Element.focus('issue_category_name');

View File

@ -9,7 +9,6 @@
<%= toggle_link l(:button_cancel), 'new-relation-form'%>
</p>
<div id="related_issue_candidates" class="autocomplete"></div>
<%= javascript_tag "observeRelatedIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @project) }')" %>
<%= javascript_tag "observeAutocompleteField('relation_issue_to_id', '#{escape_javascript auto_complete_issues_path(:id => @issue, :project_id => @project, :scope => 'all')}')" %>
<%= javascript_tag "setPredecessorFieldsVisibility();" %>

View File

@ -1,5 +1,6 @@
Element.update('relations', '<%= escape_javascript(render :partial => 'issues/relations') %>');
$('#relations').html('<%= escape_javascript(render :partial => 'issues/relations') %>');
<% if @relation.errors.empty? %>
$('relation_delay').value = ''
$('relation_issue_to_id').value = ''
$('#relation_delay').val('');
$('#relation_issue_to_id').val('');
$('#relation_issue_to_id').focus();
<% end %>

View File

@ -1 +1 @@
Element.remove('<%= "relation-#{@relation.id}" %>');
$('#relation-<%= @relation.id %>').remove();

View File

@ -3,9 +3,8 @@
<div class="splitcontent">
<div class="splitcontentleft">
<% if @issue.safe_attribute? 'status_id' %>
<p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
<%= observe_field :issue_status_id, :url => project_issue_form_path(@project, :id => @issue),
:with => "Form.serialize('issue-form')" %>
<p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), {:required => true},
:onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')" %></p>
<% else %>
<p><label><%= l(:field_status) %></label> <%= h(@issue.status.name) %></p>
@ -44,8 +43,7 @@
<div class="splitcontentright">
<% if @issue.safe_attribute? 'parent_issue_id' %>
<p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10, :required => @issue.required_attribute?('parent_issue_id') %></p>
<div id="parent_issue_candidates" class="autocomplete"></div>
<%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @issue.project) }')" %>
<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:id => @issue, :project_id => @issue.project)}')" %>
<% end %>
<% if @issue.safe_attribute? 'start_date' %>

View File

@ -8,15 +8,13 @@
<% end %>
<% if @issue.safe_attribute? 'project_id' %>
<p><%= f.select :project_id, project_tree_options_for_select(@issue.allowed_target_projects, :selected => @issue.project), :required => true %></p>
<%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue),
:with => "Form.serialize('issue-form')" %>
<p><%= f.select :project_id, project_tree_options_for_select(@issue.allowed_target_projects, :selected => @issue.project), {:required => true},
:onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')" %></p>
<% end %>
<% if @issue.safe_attribute? 'tracker_id' %>
<p><%= f.select :tracker_id, @issue.project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p>
<%= observe_field :issue_tracker_id, :url => project_issue_form_path(@project, :id => @issue),
:with => "Form.serialize('issue-form')" %>
<p><%= f.select :tracker_id, @issue.project.trackers.collect {|t| [t.name, t.id]}, {:required => true},
:onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')" %></p>
<% end %>
<% if @issue.safe_attribute? 'subject' %>
@ -26,8 +24,7 @@
<% if @issue.safe_attribute? 'description' %>
<p>
<%= f.label_for_field :description, :required => @issue.required_attribute?('description') %>
<%= link_to_function image_tag('edit.png'),
'Element.hide(this); Effect.toggle("issue_description_and_toolbar", "appear", {duration:0.3})' unless @issue.new_record? %>
<%= link_to_function image_tag('edit.png'), '$(this).hide(); $("#issue_description_and_toolbar").show()' unless @issue.new_record? %>
<%= content_tag 'span', :id => "issue_description_and_toolbar", :style => (@issue.new_record? ? nil : 'display:none') do %>
<%= f.text_area :description,
:cols => 60,

View File

@ -3,7 +3,7 @@
<div class="autoscroll">
<table class="list issues">
<thead><tr>
<th class="checkbox hide-when-print"><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;',
<th class="checkbox hide-when-print"><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(this); return false;',
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
</th>
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>

View File

@ -35,7 +35,6 @@
:as => :relation, :remote => true,
:url => {:controller => 'issue_relations', :action => 'create', :issue_id => @issue},
:method => :post,
:complete => "Form.Element.focus('relation_issue_to_id');",
:html => {:id => 'new-relation-form', :style => (@relation ? '' : 'display: none;')}
} do |f| %>
<%= render :partial => 'issue_relations/form', :locals => {:f => f}%>

View File

@ -1,9 +1,7 @@
Element.update('all_attributes', '<%= escape_javascript(render :partial => 'form') %>');
$('#all_attributes').html('<%= escape_javascript(render :partial => 'form') %>');
if ($('log_time')) {
<% if User.current.allowed_to?(:log_time, @issue.project) %>
Element.show('log_time');
$('#log_time').show();
<% else %>
Element.hide('log_time');
$('#log_time').hide();
<% end %>
}

View File

@ -17,11 +17,9 @@
<% if @allowed_projects.present? %>
<p>
<label for="issue_project_id"><%= l(:field_project) %></label>
<%= select_tag('issue[project_id]', content_tag('option', l(:label_no_change_option), :value => '') + project_tree_options_for_select(@allowed_projects, :selected => @target_project)) %>
<%= select_tag('issue[project_id]', content_tag('option', l(:label_no_change_option), :value => '') + project_tree_options_for_select(@allowed_projects, :selected => @target_project),
:onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %>
</p>
<%= observe_field :issue_project_id, :url => {:action => 'bulk_edit'},
:update => 'content',
:with => "Form.serialize('bulk_edit_form')" %>
<% end %>
<p>
<label for="issue_tracker_id"><%= l(:field_tracker) %></label>
@ -97,8 +95,7 @@
<label for='issue_parent_issue_id'><%= l(:field_parent_issue) %></label>
<%= text_field_tag 'issue[parent_issue_id]', '', :size => 10 %>
</p>
<div id="parent_issue_candidates" class="autocomplete"></div>
<%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:project_id => @project) }')" %>
<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @project)}')" %>
<% end %>
<% if @safe_attributes.include?('start_date') %>

View File

@ -0,0 +1 @@
$('#content').html('<%= escape_javascript(render :template => 'issues/bulk_edit.html') %>');

View File

@ -7,8 +7,8 @@
<p>
<label><%= radio_button_tag 'todo', 'destroy', true %> <%= l(:text_destroy_time_entries) %></label><br />
<label><%= radio_button_tag 'todo', 'nullify', false %> <%= l(:text_assign_time_entries_to_project) %></label><br />
<label><%= radio_button_tag 'todo', 'reassign', false, :onchange => 'if (this.checked) { $("reassign_to_id").focus(); }' %> <%= l(:text_reassign_time_entries) %></label>
<%= text_field_tag 'reassign_to_id', params[:reassign_to_id], :size => 6, :onfocus => '$("todo_reassign").checked=true;' %>
<label><%= radio_button_tag 'todo', 'reassign', false, :onchange => 'if (this.checked) { $("#reassign_to_id").focus(); }' %> <%= l(:text_reassign_time_entries) %></label>
<%= text_field_tag 'reassign_to_id', params[:reassign_to_id], :size => 6, :onfocus => '$("#todo_reassign").attr("checked", true);' %>
</p>
</div>
<%= submit_tag l(:button_apply) %>

View File

@ -44,7 +44,7 @@
<%= link_to l(:button_clear), { :set_filter => 1, :project_id => @project }, :class => 'icon icon-reload' %>
<% if @query.new_record? && User.current.allowed_to?(:save_queries, @project, :global => true) %>
<%= link_to_function l(:button_save),
"$('query_form').action='#{ @project ? new_project_query_path(@project) : new_query_path }'; submit_query_form('query_form')",
"$('#query_form').attr('action', '#{ @project ? new_project_query_path(@project) : new_query_path }'); submit_query_form('query_form')",
:class => 'icon icon-save' %>
<% end %>
</p>

View File

@ -39,7 +39,7 @@
<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
<%= preview_link preview_new_issue_path(:project_id => @project), 'issue-form' %>
<%= javascript_tag "Form.Element.focus('issue_subject');" %>
<%= javascript_tag "$('#issue_subject').focus();" %>
<% end %>
<div id="preview" class="wiki"></div>

View File

@ -11,8 +11,7 @@
<%= preview_link preview_edit_issue_path(:project_id => @project, :id => @journal.issue),
"journal-#{@journal.id}-form",
"journal_#{@journal.id}_preview" %> |
<%= link_to l(:button_cancel), '#', :onclick => "Element.remove('journal-#{@journal.id}-form'); " +
"Element.show('journal-#{@journal.id}-notes'); return false;" %></p>
<%= link_to l(:button_cancel), '#', :onclick => "$('#journal-#{@journal.id}-form').remove(); $('#journal-#{@journal.id}-notes').show(); return false;" %></p>
<div id="journal_<%= @journal.id %>_preview" class="wiki"></div>
<% end %>

View File

@ -1,2 +1,2 @@
Element.hide("journal-<%= @journal.id %>-notes");
Element.insert("journal-<%= @journal.id %>-notes", {'after': '<%= escape_javascript(render :partial => 'notes_form') %>'});
$("#journal-<%= @journal.id %>-notes").hide();
$("#journal-<%= @journal.id %>-notes").after('<%= escape_javascript(render :partial => 'notes_form') %>');

View File

@ -1,5 +1,3 @@
$('notes').value = "<%= raw escape_javascript(@content) %>";
Element.show('update');
Form.Element.focus('notes');
Element.scrollTo('update');
$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;
$('#notes').val("<%= raw escape_javascript(@content) %>");
showAndScrollTo("update", "notes");
$('#notes').scrollTop = $('#notes').scrollHeight - $('#notes').clientHeight;

View File

@ -1,9 +1,9 @@
<% if @journal.frozen? %>
Element.remove("change-<%= @journal.id %>");
$("#change-<%= @journal.id %>").remove();
<% else %>
Element.replace("journal-<%= @journal.id %>-notes", '<%= escape_javascript(render_notes(@journal.issue, @journal, :reply_links => authorize_for('issues', 'edit'))) %>');
Element.show("journal-<%= @journal.id %>-notes");
Element.remove("journal-<%= @journal.id %>-form");
$("#journal-<%= @journal.id %>-notes").replaceWith('<%= escape_javascript(render_notes(@journal.issue, @journal, :reply_links => authorize_for('issues', 'edit'))) %>');
$("#journal-<%= @journal.id %>-notes").show();
$("#journal-<%= @journal.id %>-form").remove();
<% end %>
<%= call_hook(:view_journals_update_js_bottom, { :journal => @journal }) %>

View File

@ -8,7 +8,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE" />
<%= csrf_meta_tag %>
<%= favicon %>
<%= stylesheet_link_tag 'application', :media => 'all' %>
<%= stylesheet_link_tag 'jquery/jquery-ui-1.8.21', 'application', :media => 'all' %>
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
<%= javascript_heads %>
<%= heads_for_theme %>

View File

@ -1,9 +1,9 @@
Element.update('tab-content-members', '<%= escape_javascript(render :partial => 'projects/settings/members') %>');
$('#tab-content-members').html('<%= escape_javascript(render :partial => 'projects/settings/members') %>');
hideOnLoad();
<% if @members.present? && @members.all? {|m| m.valid? } %>
<% @members.each do |member| %>
new Effect.Highlight("member-<%= member.id %>");
$("#member-<%= member.id %>").effect("highlight");
<% end %>
<% else %>
<% errors = @members.collect {|m| m.errors.full_messages}.flatten.uniq.join(', ') %>

View File

@ -1,2 +1,2 @@
Element.update('tab-content-members', '<%= escape_javascript(render :partial => 'projects/settings/members') %>');
$('#tab-content-members').html('<%= escape_javascript(render :partial => 'projects/settings/members') %>');
hideOnLoad();

View File

@ -1,3 +1,3 @@
Element.update('tab-content-members', '<%= escape_javascript(render :partial => 'projects/settings/members') %>');
$('#tab-content-members').html('<%= escape_javascript(render :partial => 'projects/settings/members') %>');
hideOnLoad();
new Effect.Highlight("member-<%= @member.id %>");
$("#member-<%= @member.id %>").effect("highlight");

View File

@ -1,6 +1,4 @@
$('message_subject').value = "<%= raw escape_javascript(@subject) %>";
$('message_content').value = "<%= raw escape_javascript(@content) %>";
Element.show('reply');
Form.Element.focus('message_content');
Element.scrollTo('reply');
$('message_content').scrollTop = $('message_content').scrollHeight - $('message_content').clientHeight;
$('#message_subject').val("<%= raw escape_javascript(@subject) %>");
$('#message_content').val("<%= raw escape_javascript(@content) %>");
showAndScrollTo("reply", "message_content");
$('#message_content').scrollTop = $('#message_content').scrollHeight - $('#message_content').clientHeight;

View File

@ -5,7 +5,7 @@
<%= select_tag 'block',
content_tag('option') + options_for_select(@block_options),
:id => "block-select" %>
<%= link_to l(:button_add), '#', :onclick => '$("block-form").submit()', :class => 'icon icon-add' %>
<%= link_to l(:button_add), '#', :onclick => '$("#block-form").submit()', :class => 'icon icon-add' %>
<% end %>
<% end %>
<%= link_to l(:button_back), {:action => 'page'}, :class => 'icon icon-cancel' %>
@ -34,34 +34,8 @@
<% end if @blocks['right'] %>
</div>
<%= sortable_element 'list-top',
:tag => 'div',
:only => 'mypage-box',
:handle => "handle",
:dropOnEmpty => true,
:containment => ['list-top', 'list-left', 'list-right'],
:constraint => false,
:url => { :action => "order_blocks", :group => "top" }
%>
<%= sortable_element 'list-left',
:tag => 'div',
:only => 'mypage-box',
:handle => "handle",
:dropOnEmpty => true,
:containment => ['list-top', 'list-left', 'list-right'],
:constraint => false,
:url => { :action => "order_blocks", :group => "left" }
%>
<%= sortable_element 'list-right',
:tag => 'div',
:only => 'mypage-box',
:handle => "handle",
:dropOnEmpty => true,
:containment => ['list-top', 'list-left', 'list-right'],
:constraint => false,
:url => { :action => "order_blocks", :group => "right" }
%>
<%= javascript_tag "initMyPageSortable('top', '#{ escape_javascript url_for(:action => "order_blocks", :group => "top") }');" %>
<%= javascript_tag "initMyPageSortable('left', '#{ escape_javascript url_for(:action => "order_blocks", :group => "left") }');" %>
<%= javascript_tag "initMyPageSortable('right', '#{ escape_javascript url_for(:action => "order_blocks", :group => "right") }');" %>
<% html_title(l(:label_my_page)) -%>

View File

@ -2,7 +2,7 @@
<%= link_to(l(:label_news_new),
new_project_news_path(@project),
:class => 'icon icon-add',
:onclick => 'Element.show("add-news"); Form.Element.focus("news_title"); return false;') if @project && User.current.allowed_to?(:manage_news, @project) %>
:onclick => 'showAndScrollTo("add-news", "news_title"); return false;') if @project && User.current.allowed_to?(:manage_news, @project) %>
</div>
<div id="add-news" style="display:none;">
@ -12,7 +12,7 @@
<%= render :partial => 'news/form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %>
<%= preview_link preview_news_path(:project_id => @project), 'news-form' %> |
<%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-news")' %>
<%= link_to l(:button_cancel), "#", :onclick => '$("#add-news").hide()' %>
<% end if @project %>
<div id="preview" class="wiki"></div>
</div>

View File

@ -4,7 +4,7 @@
edit_news_path(@news),
:class => 'icon icon-edit',
:accesskey => accesskey(:edit),
:onclick => 'Element.show("edit-news"); return false;') if User.current.allowed_to?(:manage_news, @project) %>
:onclick => '$("#edit-news").show(); return false;') if User.current.allowed_to?(:manage_news, @project) %>
<%= delete_link news_path(@news) if User.current.allowed_to?(:manage_news, @project) %>
</div>
@ -17,7 +17,7 @@
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %>
<%= preview_link preview_news_path(:project_id => @project), 'news-form' %> |
<%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("edit-news"); return false;' %>
<%= link_to l(:button_cancel), "#", :onclick => '$("#edit-news").hide(); return false;' %>
<% end %>
<div id="preview" class="wiki"></div>
</div>

View File

@ -4,5 +4,5 @@
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %>
<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
<%= javascript_tag "Form.Element.focus('project_name');" %>
<%= javascript_tag "$('#project_name').focus();" %>
<% end %>

View File

@ -29,13 +29,13 @@
<%= hidden_field_tag 'membership[role_ids][]', '' %>
<p><%= submit_tag l(:button_change), :class => "small" %>
<%= link_to_function l(:button_cancel),
"$('member-#{member.id}-roles').show(); $('member-#{member.id}-roles-form').hide(); return false;"
"$('#member-#{member.id}-roles').show(); $('#member-#{member.id}-roles-form').hide(); return false;"
%></p>
<% end %>
</td>
<td class="buttons">
<%= link_to_function l(:button_edit),
"$('member-#{member.id}-roles').hide(); $('member-#{member.id}-roles-form').show(); return false;",
"$('#member-#{member.id}-roles').hide(); $('#member-#{member.id}-roles-form').show(); return false;",
:class => 'icon icon-edit' %>
<%= delete_link membership_path(member),
:remote => true,
@ -55,23 +55,11 @@
<div class="splitcontentright">
<% if roles.any? && principals.any? %>
<%= form_for(@member, {:as => :membership, :remote => true,
:url => project_memberships_path(@project), :method => :post,
:loading => '$(\'member-add-submit\').disable();',
:complete => 'if($(\'member-add-submit\')) $(\'member-add-submit\').enable();'
} ) do |f| %>
<%= form_for(@member, {:as => :membership, :url => project_memberships_path(@project), :remote => true, :method => :post}) do |f| %>
<fieldset><legend><%=l(:label_member_new)%></legend>
<p><%= label_tag "principal_search", l(:label_principal_search) %><%= text_field_tag 'principal_search', nil %></p>
<%= observe_field(:principal_search,
:frequency => 0.5,
:update => :principals,
:url => autocomplete_project_memberships_path(@project),
:method => :get,
:before => '$("principal_search").addClassName("ajax-loading")',
:complete => '$("principal_search").removeClassName("ajax-loading")',
:with => 'q')
%>
<%= javascript_tag "observeSearchfield('principal_search', 'principals', '#{ escape_javascript autocomplete_project_memberships_path(@project) }')" %>
<div id="principals">
<%= principals_check_box_tags 'membership[user_ids][]', principals %>

View File

@ -50,4 +50,4 @@
</tr>
</table>
<%= hidden_field_tag 'f[]', '' %>
<%= javascript_tag 'Event.observe(window,"load",apply_filters_observer);' %>
<%= javascript_tag '$(document).ready(function(){observeIssueFilters();});' %>

View File

@ -8,7 +8,7 @@
<% if User.current.admin? || User.current.allowed_to?(:manage_public_queries, @project) %>
<p><label for="query_is_public"><%=l(:field_is_public)%></label>
<%= check_box 'query', 'is_public',
:onchange => (User.current.admin? ? nil : 'if (this.checked) {$("query_is_for_all").checked = false; $("query_is_for_all").disabled = true;} else {$("query_is_for_all").disabled = false;}') %></p>
:onchange => (User.current.admin? ? nil : 'if (this.checked) {$("#query_is_for_all").removeAttr("checked"); $("#query_is_for_all").attr("disabled", true);} else {$("#query_is_for_all").removeAttr("disabled");}') %></p>
<% end %>
<p><label for="query_is_for_all"><%=l(:field_is_for_all)%></label>
@ -17,7 +17,7 @@
<p><label for="query_default_columns"><%=l(:label_default_columns)%></label>
<%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
:onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p>
:onclick => 'if (this.checked) {$("#columns").hide();} else {$("#columns").show();}' %></p>
<p><label for="query_group_by"><%= l(:field_group_by) %></label>
<%= select 'query', 'group_by', @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, :include_blank => true %></p>

View File

@ -7,22 +7,14 @@
<td style="padding-left: <%=18 * depth%>px;" class="<%=
@repository.report_last_commit ? "filename" : "filename_no_report" %>";>
<% if entry.is_dir? %>
<span class="expander" onclick="<%= remote_function(
:url => {
<span class="expander" onclick="scmEntryClick('<%= tr_id %>', '<%= escape_javascript(url_for(
:action => 'show',
:id => @project,
:repository_id => @repository.identifier_param,
:path => to_path_param(ent_path),
:rev => @rev,
:depth => (depth + 1),
:parent_id => tr_id
},
:method => :get,
:update => { :success => tr_id },
:position => :after,
:success => "scmEntryLoaded('#{tr_id}')",
:condition => "scmEntryClick('#{tr_id}')"
) %>">&nbsp</span>
:parent_id => tr_id)) %>');">&nbsp</span>
<% end %>
<%= link_to h(ent_name),
{:action => (entry.is_dir? ? 'show' : 'changes'), :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev},

View File

@ -29,7 +29,6 @@
:id => @project, :repository_id => @repository.identifier_param,
:rev => @changeset.identifier},
:method => :post,
:complete => "Form.Element.focus('issue_id');",
:html => {:id => 'new-relation-form', :style => (@issue ? '' : 'display: none;')}) do |f| %>
<%= l(:label_issue) %> #<%= text_field_tag 'issue_id', '', :size => 10 %>
<%= submit_tag l(:button_add) %>

View File

@ -2,17 +2,16 @@
<%= javascript_include_tag 'revision_graph.js' %>
<script type="text/javascript" charset="utf-8">
['load', 'resize'].each(function(window_event) {
Event.observe(window, window_event, function(){
function revisionGraphHandler(){
drawRevisionGraph(
document.getElementById('holder'),
<%= commits.to_json.html_safe %>,
<%= space %>);
});
});
<%= space %>
);
}
$(document).ready(revisionGraphHandler);
$(window).resize(revisionGraphHandler);
</script>
<div id="holder" class="revision-graph"></div>

View File

@ -37,8 +37,8 @@ end %>
<%= content_tag(:td, :class => 'id', :style => id_style) do %>
<%= link_to_revision(changeset, @repository) %>
<% end %>
<td class="checkbox"><%= radio_button_tag('rev', changeset.identifier, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
<td class="checkbox"><%= radio_button_tag('rev_to', changeset.identifier, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
<td class="checkbox"><%= radio_button_tag('rev', changeset.identifier, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('#cbto-#{line_num+1}').attr('checked',true);") if show_diff && (line_num < revisions.size) %></td>
<td class="checkbox"><%= radio_button_tag('rev_to', changeset.identifier, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('#cb-#{line_num}').attr('checked')) {$('#cb-#{line_num-1}').attr('checked',true);}") if show_diff && (line_num > 1) %></td>
<td class="committed_on"><%= format_time(changeset.committed_on) %></td>
<td class="author"><%= h truncate(changeset.author.to_s, :length => 30) %></td>
<td class="comments"><%= textilizable(truncate_at_line_break(changeset.comments)) %></td>

View File

@ -1,6 +1,7 @@
<% if @issue %>
Element.update('related-issues', '<%= escape_javascript(render :partial => "related_issues") %>');
Effect.highlight('related-issue-<%= @issue.id %>');
$('#related-issues').html('<%= escape_javascript(render :partial => "related_issues") %>');
$('#related-issue-<%= @issue.id %>').effect("highlight");
$('#issue_id').focus();
<% else %>
alert("<%= escape_javascript(l(:label_issue) + ' ' + l('activerecord.errors.messages.invalid')) %>");
<% end %>

View File

@ -1,5 +1,5 @@
<h2><%= l(:label_repository) %></h2>
<%= labelled_form_for :repository, @repository, :url => repository_path(@repository), :html => {:method => :put} do |f| %>
<%= labelled_form_for :repository, @repository, :url => repository_path(@repository), :html => {:method => :put, :id => 'repository-form'} do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<% end %>

View File

@ -1,5 +1,5 @@
<h2><%= l(:label_repository_new) %></h2>
<%= labelled_form_for :repository, @repository, :url => project_repositories_path(@project) do |f| %>
<%= labelled_form_for :repository, @repository, :url => project_repositories_path(@project), :html => {:id => 'repository-form'} do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<% end %>

View File

@ -0,0 +1 @@
$('#content').html('<%= escape_javascript(render :template => 'repositories/new.html') %>');

View File

@ -1 +1 @@
Element.remove('related-issue-<%= @issue.id %>');
$('#related-issue-<%= @issue.id %>').remove();

View File

@ -4,7 +4,7 @@
<%= form_tag({}, :method => :get) do %>
<%= label_tag "search-input", l(:description_search), :class => "hidden-for-sighted" %>
<p><%= text_field_tag 'q', @question, :size => 60, :id => 'search-input' %>
<%= javascript_tag "Field.focus('search-input')" %>
<%= javascript_tag "$('#search-input').focus()" %>
<%= project_select_tag %>
<%= hidden_field_tag 'all_words', '', :id => nil %>
<label><%= check_box_tag 'all_words', 1, @all_words %> <%= l(:label_all_words) %></label>

View File

@ -9,12 +9,12 @@
<div class="box tabular settings">
<p><%= setting_check_box :mail_handler_api_enabled,
:onclick => "if (this.checked) { Form.Element.enable('settings_mail_handler_api_key'); } else { Form.Element.disable('settings_mail_handler_api_key'); }"%></p>
:onclick => "if (this.checked) { $('#settings_mail_handler_api_key').removeAttr('disabled'); } else { $('#settings_mail_handler_api_key').attr('disabled', true); }"%></p>
<p><%= setting_text_field :mail_handler_api_key, :size => 30,
:id => 'settings_mail_handler_api_key',
:disabled => !Setting.mail_handler_api_enabled? %>
<%= link_to_function l(:label_generate_key), "if ($('settings_mail_handler_api_key').disabled == false) { $('settings_mail_handler_api_key').value = randomKey(20) }" %>
<%= link_to_function l(:label_generate_key), "if (!$('#settings_mail_handler_api_key').attr('disabled')) { $('#settings_mail_handler_api_key').val(randomKey(20)) }" %>
</p>
</div>

View File

@ -49,7 +49,7 @@
<p><%= setting_check_box :sys_api_enabled,
:onclick =>
"if (this.checked) { Form.Element.enable('settings_sys_api_key'); } else { Form.Element.disable('settings_sys_api_key'); }" %></p>
"if (this.checked) { $('#settings_sys_api_key').removeAttr('disabled'); } else { $('#settings_sys_api_key').attr('disabled', true); }" %></p>
<p><%= setting_text_field :sys_api_key,
:size => 30,
@ -57,7 +57,7 @@
:disabled => !Setting.sys_api_enabled?,
:label => :setting_mail_handler_api_key %>
<%= link_to_function l(:label_generate_key),
"if ($('settings_sys_api_key').disabled == false) { $('settings_sys_api_key').value = randomKey(20) }" %>
"if (!$('#settings_sys_api_key').attr('disabled')) { $('#settings_sys_api_key').val(randomKey(20)) }" %>
</p>
<p><%= setting_text_field :repository_log_display_limit, :size => 6 %></p>
@ -85,7 +85,7 @@
<p><%= setting_check_box :commit_logtime_enabled,
:onclick =>
"if (this.checked) { Form.Element.enable('settings_commit_logtime_activity_id'); } else { Form.Element.disable('settings_commit_logtime_activity_id'); }"%></p>
"if (this.checked) { $('#settings_commit_logtime_activity_id').removeAttr('disabled'); } else { $('#settings_commit_logtime_activity_id').attr('disabled', true); }"%></p>
<p><%= setting_select :commit_logtime_activity_id,
[[l(:label_default), 0]] +

View File

@ -3,16 +3,16 @@
<div>
<p>
<%= label_tag "period_type_list", l(:description_date_range_list), :class => "hidden-for-sighted" %>
<%= radio_button_tag 'period_type', '1', !@free_period, :onclick => 'Form.Element.disable("from");Form.Element.disable("to");Form.Element.enable("period");', :id => "period_type_list"%>
<%= radio_button_tag 'period_type', '1', !@free_period, :onclick => '$("#from,#to").attr("disabled", true);$("#period").removeAttr("disabled");', :id => "period_type_list"%>
<%= select_tag 'period', options_for_period_select(params[:period]),
:onchange => 'this.form.submit();',
:onfocus => '$("period_type_1").checked = true;',
:onfocus => '$("#period_type_1").attr("checked", true);',
:disabled => @free_period %>
</p>
<p>
<%= label_tag "period_type_interval", l(:description_date_range_interval), :class => "hidden-for-sighted" %>
<%= radio_button_tag 'period_type', '2', @free_period, :onclick => 'Form.Element.enable("from");Form.Element.enable("to");Form.Element.disable("period");', :id => "period_type_interval" %>
<span onclick="$('period_type_interval').checked = true;Form.Element.enable('from');Form.Element.enable('to');Form.Element.disable('period');">
<%= radio_button_tag 'period_type', '2', @free_period, :onclick => '$("#from,#to").removeAttr("disabled");$("#period").attr("disabled", true);', :id => "period_type_interval" %>
<span onclick="$('#period_type_interval').attr('checked', true);$('#from,#to').removeAttr('disabled');$('#period').attr('disabled', true);">
<%= l(:label_date_from_to,
:start => ((label_tag "from", l(:description_date_from), :class => "hidden-for-sighted") +
text_field_tag('from', @from, :size => 10, :disabled => !@free_period) + calendar_for('from')),
@ -23,7 +23,7 @@
</div>
</fieldset>
<p class="buttons">
<%= link_to_function l(:button_apply), '$("query_form").submit()', :class => 'icon icon-checked' %>
<%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %>
<%= link_to l(:button_clear), {:controller => controller_name, :action => action_name, :project_id => @project, :issue_id => @issue}, :class => 'icon icon-reload' %>
</p>

View File

@ -7,7 +7,7 @@
<th class="checkbox hide-when-print">
<%= link_to image_tag('toggle_check.png'),
{},
:onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;',
:onclick => 'toggleIssuesSelection(this); return false;',
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
</th>
<%= sort_header_tag('spent_on', :caption => l(:label_date), :default_order => 'desc') %>

View File

@ -25,7 +25,7 @@
<fieldset class="box tabular">
<legend><%=l(:label_authentication)%></legend>
<% unless @auth_sources.empty? %>
<p><%= f.select :auth_source_id, ([[l(:label_internal), ""]] + @auth_sources.collect { |a| [a.name, a.id] }), {}, :onchange => "if (this.value=='') {Element.show('password_fields');} else {Element.hide('password_fields');}" %></p>
<p><%= f.select :auth_source_id, ([[l(:label_internal), ""]] + @auth_sources.collect { |a| [a.name, a.id] }), {}, :onchange => "if (this.value=='') {$('#password_fields').show();} else {$('#password_fields').hide();}" %></p>
<% end %>
<div id="password_fields" style="<%= 'display:none;' if @user.auth_source %>">
<p><%= f.password_field :password, :required => true, :size => 25 %>

View File

@ -4,7 +4,7 @@
'user[mail_notification]',
options_for_select(
user_mail_notification_options(@user), @user.mail_notification),
:onchange => 'if (this.value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}'
:onchange => 'if (this.value == "selected") {$("#notified-projects").show();} else {$("#notified-projects").hide();}'
) %>
</p>
<%= content_tag 'div', :id => 'notified-projects', :style => (@user.mail_notification == 'selected' ? '' : 'display:none;') do %>

View File

@ -30,13 +30,13 @@
<%= hidden_field_tag 'membership[role_ids][]', '' %>
<p><%= submit_tag l(:button_change) %>
<%= link_to_function l(:button_cancel),
"$('member-#{membership.id}-roles').show(); $('member-#{membership.id}-roles-form').hide(); return false;"
"$('#member-#{membership.id}-roles').show(); $('#member-#{membership.id}-roles-form').hide(); return false;"
%></p>
<% end %>
</td>
<td class="buttons">
<%= link_to_function l(:button_edit),
"$('member-#{membership.id}-roles').hide(); $('member-#{membership.id}-roles-form').show(); return false;",
"$('#member-#{membership.id}-roles').hide(); $('#member-#{membership.id}-roles-form').show(); return false;",
:class => 'icon icon-edit'
%>
<%= delete_link user_membership_path(@user, membership), :remote => true if membership.deletable? %>

View File

@ -1 +1 @@
Element.update('tab-content-memberships', '<%= escape_javascript(render :partial => 'users/memberships') %>');
$('#tab-content-memberships').html('<%= escape_javascript(render :partial => 'users/memberships') %>');

View File

@ -1,6 +1,6 @@
<% if @membership.valid? %>
Element.update("tab-content-memberships", '<%= escape_javascript(render :partial => 'users/memberships') %>');
new Effect.Highlight("member-<%= @membership.id %>")
$('#tab-content-memberships').html('<%= escape_javascript(render :partial => 'users/memberships') %>');
$("#member-<%= @membership.id %>").effect("highlight");
<% else %>
alert('<%= escape_javascript l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')) %>');
<% end %>

View File

@ -5,8 +5,7 @@
select_tag('status_by',
status_by_options_for_select(criteria),
:id => 'status_by_select',
:onchange => remote_function(:url => status_by_version_path(version),
:with => "Form.serialize('status_by_form')"))).html_safe %>
:data => {:remote => true, :method => 'post', :url => status_by_version_path(version)})).html_safe %>
</legend>
<% if counts.empty? %>
<p><em><%= l(:label_no_data) %></em></p>

View File

@ -1,3 +1,3 @@
hideModal();
<% select = content_tag('select', content_tag('option') + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]') %>
Element.replace('issue_fixed_version_id', '<%= escape_javascript(select) %>');
$('#issue_fixed_version_id').replaceWith('<%= escape_javascript(select) %>');

View File

@ -54,7 +54,7 @@
<% if @completed_versions.present? %>
<p>
<%= link_to_function l(:label_completed_versions),
'Element.toggleClassName("toggle-completed-versions", "collapsed"); Element.toggle("completed-versions")',
'$("#toggle-completed-versions").toggleClass("collapsed"); $("#completed-versions").toggle()',
:id => 'toggle-completed-versions', :class => 'collapsible collapsed' %><br />
<span id="completed-versions" style="display:none;">
<%= @completed_versions.map {|version| link_to format_version_name(version), version_path(version)}.join("<br />\n").html_safe %>

View File

@ -1,3 +1,2 @@
Element.update('ajax-modal', '<%= escape_javascript(render :partial => 'versions/new_modal') %>');
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'versions/new_modal') %>');
showModal('ajax-modal', '600px');
Form.Element.focus('version_name');

View File

@ -1 +1 @@
Element.update('status_by', '<%= escape_javascript(render_issue_status_by(@version, params[:status_by])) %>');
$('#status_by').html('<%= escape_javascript(render_issue_status_by(@version, params[:status_by])) %>');

View File

@ -9,18 +9,10 @@
:id => 'new-watcher-form') do %>
<p><%= label_tag 'user_search', l(:label_user_search) %><%= text_field_tag 'user_search', nil %></p>
<%= observe_field(:user_search,
:frequency => 0.5,
:update => :users_for_watcher,
:method => :get,
:before => '$("user_search").addClassName("ajax-loading")',
:complete => '$("user_search").removeClassName("ajax-loading")',
:url => {
:controller => 'watchers',
<%= javascript_tag "observeSearchfield('user_search', 'users_for_watcher', '#{ escape_javascript url_for(:controller => 'watchers',
:action => 'autocomplete_for_user',
:object_type => watched.class.name.underscore,
:object_id => watched},
:with => 'q') %>
:object_id => watched) }')" %>
<div id="users_for_watcher">
<%= principals_check_box_tags 'watcher[user_ids][]', (watched ? watched.addable_watcher_users : User.active.all(:limit => 100)) %>

View File

@ -1,2 +1,2 @@
<% selector = ".#{watcher_css(watched)}" %>
$$("<%= selector %>").each(function(el){el.update("<%= escape_javascript watcher_link(watched, user) %>")});
$("<%= selector %>").each(function(){$(this).html("<%= escape_javascript watcher_link(watched, user) %>")});

View File

@ -1,4 +1,4 @@
<% @users.each do |user| %>
$$("#issue_watcher_user_ids_<%= user.id %>").each(function(el){el.remove();});
$("#issue_watcher_user_ids_<%= user.id %>").remove();
<% end %>
Element.insert('watchers_inputs', '<%= escape_javascript(watchers_checkboxes(nil, @users, true)) %>');
$('#watchers_inputs').append('<%= escape_javascript(watchers_checkboxes(nil, @users, true)) %>');

View File

@ -1,2 +1,2 @@
Element.update('ajax-modal', '<%= escape_javascript(render(:partial => 'watchers/new', :locals => {:watched => @watched})) %>');
Element.update('watchers', '<%= escape_javascript(render(:partial => 'watchers/watchers', :locals => {:watched => @watched})) %>');
$('#ajax-modal').html('<%= escape_javascript(render(:partial => 'watchers/new', :locals => {:watched => @watched})) %>');
$('#watchers').html('<%= escape_javascript(render(:partial => 'watchers/watchers', :locals => {:watched => @watched})) %>');

View File

@ -1 +1 @@
Element.update('watchers', '<%= escape_javascript(render(:partial => 'watchers/watchers', :locals => {:watched => @watched})) %>');
$('#watchers').html('<%= escape_javascript(render(:partial => 'watchers/watchers', :locals => {:watched => @watched})) %>');

View File

@ -1,3 +1,3 @@
Element.update('ajax-modal', '<%= escape_javascript(render :partial => 'watchers/new', :locals => {:watched => @watched}) %>');
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'watchers/new', :locals => {:watched => @watched}) %>');
showModal('ajax-modal', '400px');
$('ajax-modal').addClassName('new-watcher');
$('#ajax-modal').addClass('new-watcher');

View File

@ -12,7 +12,7 @@
<label><%= radio_button_tag 'todo', 'reassign', false %> <%= l(:text_wiki_page_reassign_children) %></label>:
<%= label_tag "reassign_to_id", l(:description_wiki_subpages_reassign), :class => "hidden-for-sighted" %>
<%= select_tag 'reassign_to_id', wiki_page_options_for_select(@reassignable_to),
:onclick => "$('todo_reassign').checked = true;" %>
:onclick => "$('#todo_reassign').attr('checked', true);" %>
<% end %>
</p>
</div>

View File

@ -23,7 +23,7 @@
<% @versions.each do |ver| %>
<tr class="wiki-page-version <%= cycle("odd", "even") %>">
<td class="id"><%= link_to h(ver.version), :action => 'show', :id => @page.title, :project_id => @page.project, :version => ver.version %></td>
<td class="checkbox"><%= radio_button_tag('version', ver.version, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < @versions.size) %></td>
<td class="checkbox"><%= radio_button_tag('version', ver.version, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('#cbto-#{line_num+1}').attr('checked', true);") if show_diff && (line_num < @versions.size) %></td>
<td class="checkbox"><%= radio_button_tag('version_from', ver.version, (line_num==2), :id => "cbto-#{line_num}") if show_diff && (line_num > 1) %></td>
<td class="updated_on"><%= format_time(ver.updated_on) %></td>
<td class="author"><%= link_to_user ver.author %></td>

View File

@ -40,7 +40,7 @@
<% if @editable && authorize_for('wiki', 'add_attachment') %>
<div id="wiki_add_attachment">
<p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
<p><%= link_to l(:label_attachment_new), {}, :onclick => "$('#add_attachment_form').show(); return false;",
:id => 'attach_files_link' %></p>
<%= form_tag({:controller => 'wiki', :action => 'add_attachment',
:project_id => @project, :id => @page.title},
@ -50,7 +50,7 @@
<p><%= render :partial => 'attachments/form' %></p>
</div>
<%= submit_tag l(:button_add) %>
<%= link_to l(:button_cancel), {}, :onclick => "Element.hide('add_attachment_form'); Element.show('attach_files_link'); return false;" %>
<%= link_to l(:button_cancel), {}, :onclick => "$('#add_attachment_form').hide(); return false;" %>
<% end %>
</div>
<% end %>

View File

@ -1 +1 @@
Element.update('tab-content-wiki', '<%= escape_javascript(render :partial => 'projects/settings/wiki') %>');
$('#tab-content-wiki').html('<%= escape_javascript(render :partial => 'projects/settings/wiki') %>');

View File

@ -41,7 +41,7 @@
<%= render :partial => 'form', :locals => {:name => 'author', :workflows => @workflows['author']} %>
</div>
</fieldset>
<%= javascript_tag "hideFieldset($('author_workflows'))" unless @workflows['author'].present? %>
<%= javascript_tag "hideFieldset($('#author_workflows'))" unless @workflows['author'].present? %>
<fieldset class="collapsible" style="padding: 0;">
<legend onclick="toggleFieldset(this);"><%= l(:label_additional_workflow_transitions_for_assignee) %></legend>
@ -49,7 +49,7 @@
<%= render :partial => 'form', :locals => {:name => 'assignee', :workflows => @workflows['assignee']} %>
</div>
</fieldset>
<%= javascript_tag "hideFieldset($('assignee_workflows'))" unless @workflows['assignee'].present? %>
<%= javascript_tag "hideFieldset($('#assignee_workflows'))" unless @workflows['assignee'].present? %>
</div>
<%= submit_tag l(:button_save) %>
<% end %>

View File

@ -1,13 +0,0 @@
# Prototype Legacy Helper
This plugin adds support for `form_remote_tag`, etc from Rails 2 to Rails 3.
## Installation
Either add the following to your `Gemfile` and run `bundle`:
gem 'prototype_legacy_helper', '0.0.0', :git => 'git://github.com/rails/prototype_legacy_helper.git'
or run the following command to vendor the plugin within your app:
rails plugin install git://github.com/rails/prototype_legacy_helper.git

View File

@ -1 +0,0 @@
require 'prototype_legacy_helper'

View File

@ -1,432 +0,0 @@
module PrototypeHelper
# Creates a button with an onclick event which calls a remote action
# via XMLHttpRequest
# The options for specifying the target with :url
# and defining callbacks is the same as link_to_remote.
def button_to_remote(name, options = {}, html_options = {})
button_to_function(name, remote_function(options), html_options)
end
# Returns a button input tag with the element name of +name+ and a value (i.e., display text) of +value+
# that will submit form using XMLHttpRequest in the background instead of a regular POST request that
# reloads the page.
#
# # Create a button that submits to the create action
# #
# # Generates: <input name="create_btn" onclick="new Ajax.Request('/testing/create',
# # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
# # return false;" type="button" value="Create" />
# <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
#
# # Submit to the remote action update and update the DIV succeed or fail based
# # on the success or failure of the request
# #
# # Generates: <input name="update_btn" onclick="new Ajax.Updater({success:'succeed',failure:'fail'},
# # '/testing/update', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
# # return false;" type="button" value="Update" />
# <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
# :update => { :success => "succeed", :failure => "fail" }
#
# <tt>options</tt> argument is the same as in form_remote_tag.
def submit_to_remote(name, value, options = {})
options[:with] ||= 'Form.serialize(this.form)'
html_options = options.delete(:html) || {}
html_options[:name] = name
button_to_remote(value, options, html_options)
end
# Returns a link to a remote action defined by <tt>options[:url]</tt>
# (using the url_for format) that's called in the background using
# XMLHttpRequest. The result of that request can then be inserted into a
# DOM object whose id can be specified with <tt>options[:update]</tt>.
# Usually, the result would be a partial prepared by the controller with
# render :partial.
#
# Examples:
# # Generates: <a href="#" onclick="new Ajax.Updater('posts', '/blog/destroy/3', {asynchronous:true, evalScripts:true});
# # return false;">Delete this post</a>
# link_to_remote "Delete this post", :update => "posts",
# :url => { :action => "destroy", :id => post.id }
#
# # Generates: <a href="#" onclick="new Ajax.Updater('emails', '/mail/list_emails', {asynchronous:true, evalScripts:true});
# # return false;"><img alt="Refresh" src="/images/refresh.png?" /></a>
# link_to_remote(image_tag("refresh"), :update => "emails",
# :url => { :action => "list_emails" })
#
# You can override the generated HTML options by specifying a hash in
# <tt>options[:html]</tt>.
#
# link_to_remote "Delete this post", :update => "posts",
# :url => post_url(@post), :method => :delete,
# :html => { :class => "destructive" }
#
# You can also specify a hash for <tt>options[:update]</tt> to allow for
# easy redirection of output to an other DOM element if a server-side
# error occurs:
#
# Example:
# # Generates: <a href="#" onclick="new Ajax.Updater({success:'posts',failure:'error'}, '/blog/destroy/5',
# # {asynchronous:true, evalScripts:true}); return false;">Delete this post</a>
# link_to_remote "Delete this post",
# :url => { :action => "destroy", :id => post.id },
# :update => { :success => "posts", :failure => "error" }
#
# Optionally, you can use the <tt>options[:position]</tt> parameter to
# influence how the target DOM element is updated. It must be one of
# <tt>:before</tt>, <tt>:top</tt>, <tt>:bottom</tt>, or <tt>:after</tt>.
#
# The method used is by default POST. You can also specify GET or you
# can simulate PUT or DELETE over POST. All specified with <tt>options[:method]</tt>
#
# Example:
# # Generates: <a href="#" onclick="new Ajax.Request('/person/4', {asynchronous:true, evalScripts:true, method:'delete'});
# # return false;">Destroy</a>
# link_to_remote "Destroy", :url => person_url(:id => person), :method => :delete
#
# By default, these remote requests are processed asynchronous during
# which various JavaScript callbacks can be triggered (for progress
# indicators and the likes). All callbacks get access to the
# <tt>request</tt> object, which holds the underlying XMLHttpRequest.
#
# To access the server response, use <tt>request.responseText</tt>, to
# find out the HTTP status, use <tt>request.status</tt>.
#
# Example:
# # Generates: <a href="#" onclick="new Ajax.Request('/words/undo?n=33', {asynchronous:true, evalScripts:true,
# # onComplete:function(request){undoRequestCompleted(request)}}); return false;">hello</a>
# word = 'hello'
# link_to_remote word,
# :url => { :action => "undo", :n => word_counter },
# :complete => "undoRequestCompleted(request)"
#
# The callbacks that may be specified are (in order):
#
# <tt>:loading</tt>:: Called when the remote document is being
# loaded with data by the browser.
# <tt>:loaded</tt>:: Called when the browser has finished loading
# the remote document.
# <tt>:interactive</tt>:: Called when the user can interact with the
# remote document, even though it has not
# finished loading.
# <tt>:success</tt>:: Called when the XMLHttpRequest is completed,
# and the HTTP status code is in the 2XX range.
# <tt>:failure</tt>:: Called when the XMLHttpRequest is completed,
# and the HTTP status code is not in the 2XX
# range.
# <tt>:complete</tt>:: Called when the XMLHttpRequest is complete
# (fires after success/failure if they are
# present).
#
# You can further refine <tt>:success</tt> and <tt>:failure</tt> by
# adding additional callbacks for specific status codes.
#
# Example:
# # Generates: <a href="#" onclick="new Ajax.Request('/testing/action', {asynchronous:true, evalScripts:true,
# # on404:function(request){alert('Not found...? Wrong URL...?')},
# # onFailure:function(request){alert('HTTP Error ' + request.status + '!')}}); return false;">hello</a>
# link_to_remote word,
# :url => { :action => "action" },
# 404 => "alert('Not found...? Wrong URL...?')",
# :failure => "alert('HTTP Error ' + request.status + '!')"
#
# A status code callback overrides the success/failure handlers if
# present.
#
# If you for some reason or another need synchronous processing (that'll
# block the browser while the request is happening), you can specify
# <tt>options[:type] = :synchronous</tt>.
#
# You can customize further browser side call logic by passing in
# JavaScript code snippets via some optional parameters. In their order
# of use these are:
#
# <tt>:confirm</tt>:: Adds confirmation dialog.
# <tt>:condition</tt>:: Perform remote request conditionally
# by this expression. Use this to
# describe browser-side conditions when
# request should not be initiated.
# <tt>:before</tt>:: Called before request is initiated.
# <tt>:after</tt>:: Called immediately after request was
# initiated and before <tt>:loading</tt>.
# <tt>:submit</tt>:: Specifies the DOM element ID that's used
# as the parent of the form elements. By
# default this is the current form, but
# it could just as well be the ID of a
# table row or any other DOM element.
# <tt>:with</tt>:: A JavaScript expression specifying
# the parameters for the XMLHttpRequest.
# Any expressions should return a valid
# URL query string.
#
# Example:
#
# :with => "'name=' + $('name').value"
#
# You can generate a link that uses AJAX in the general case, while
# degrading gracefully to plain link behavior in the absence of
# JavaScript by setting <tt>html_options[:href]</tt> to an alternate URL.
# Note the extra curly braces around the <tt>options</tt> hash separate
# it as the second parameter from <tt>html_options</tt>, the third.
#
# Example:
# link_to_remote "Delete this post",
# { :update => "posts", :url => { :action => "destroy", :id => post.id } },
# :href => url_for(:action => "destroy", :id => post.id)
def link_to_remote(name, options = {}, html_options = nil)
link_to_function(name, remote_function(options), html_options || options.delete(:html))
end
# Returns a form tag that will submit using XMLHttpRequest in the
# background instead of the regular reloading POST arrangement. Even
# though it's using JavaScript to serialize the form elements, the form
# submission will work just like a regular submission as viewed by the
# receiving side (all elements available in <tt>params</tt>). The options for
# specifying the target with <tt>:url</tt> and defining callbacks is the same as
# +link_to_remote+.
#
# A "fall-through" target for browsers that doesn't do JavaScript can be
# specified with the <tt>:action</tt>/<tt>:method</tt> options on <tt>:html</tt>.
#
# Example:
# # Generates:
# # <form action="/some/place" method="post" onsubmit="new Ajax.Request('',
# # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">
# form_remote_tag :html => { :action =>
# url_for(:controller => "some", :action => "place") }
#
# The Hash passed to the <tt>:html</tt> key is equivalent to the options (2nd)
# argument in the FormTagHelper.form_tag method.
#
# By default the fall-through action is the same as the one specified in
# the <tt>:url</tt> (and the default method is <tt>:post</tt>).
#
# form_remote_tag also takes a block, like form_tag:
# # Generates:
# # <form action="/" method="post" onsubmit="new Ajax.Request('/',
# # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)});
# # return false;"> <div><input name="commit" type="submit" value="Save" /></div>
# # </form>
# <% form_remote_tag :url => '/posts' do -%>
# <div><%= submit_tag 'Save' %></div>
# <% end -%>
def form_remote_tag(options = {}, &block)
options[:form] = true
options[:html] ||= {}
options[:html][:onsubmit] =
(options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") +
"#{remote_function(options)}; return false;"
form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &block)
end
# Creates a form that will submit using XMLHttpRequest in the background
# instead of the regular reloading POST arrangement and a scope around a
# specific resource that is used as a base for questioning about
# values for the fields.
#
# === Resource
#
# Example:
# <% remote_form_for(@post) do |f| %>
# ...
# <% end %>
#
# This will expand to be the same as:
#
# <% remote_form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
# ...
# <% end %>
#
# === Nested Resource
#
# Example:
# <% remote_form_for([@post, @comment]) do |f| %>
# ...
# <% end %>
#
# This will expand to be the same as:
#
# <% remote_form_for :comment, @comment, :url => post_comment_path(@post, @comment), :html => { :method => :put, :class => "edit_comment", :id => "edit_comment_45" } do |f| %>
# ...
# <% end %>
#
# If you don't need to attach a form to a resource, then check out form_remote_tag.
#
# See FormHelper#form_for for additional semantics.
def remote_form_for(record_or_name_or_array, *args, &proc)
options = args.extract_options!
case record_or_name_or_array
when String, Symbol
object_name = record_or_name_or_array
when Array
object = record_or_name_or_array.last
object_name = ActiveModel::Naming.singular(object)
apply_form_for_options!(record_or_name_or_array, options)
args.unshift object
else
object = record_or_name_or_array
object_name = ActiveModel::Naming.singular(record_or_name_or_array)
apply_form_for_options!(object, options)
args.unshift object
end
form_remote_tag options do
fields_for object_name, *(args << options), &proc
end
end
alias_method :form_remote_for, :remote_form_for
# Returns '<tt>eval(request.responseText)</tt>' which is the JavaScript function
# that +form_remote_tag+ can call in <tt>:complete</tt> to evaluate a multiple
# update return document using +update_element_function+ calls.
def evaluate_remote_response
"eval(request.responseText)"
end
# Observes the field with the DOM ID specified by +field_id+ and calls a
# callback when its contents have changed. The default callback is an
# Ajax call. By default the value of the observed field is sent as a
# parameter with the Ajax call.
#
# Example:
# # Generates: new Form.Element.Observer('suggest', 0.25, function(element, value) {new Ajax.Updater('suggest',
# # '/testing/find_suggestion', {asynchronous:true, evalScripts:true, parameters:'q=' + value})})
# <%= observe_field :suggest, :url => { :action => :find_suggestion },
# :frequency => 0.25,
# :update => :suggest,
# :with => 'q'
# %>
#
# Required +options+ are either of:
# <tt>:url</tt>:: +url_for+-style options for the action to call
# when the field has changed.
# <tt>:function</tt>:: Instead of making a remote call to a URL, you
# can specify javascript code to be called instead.
# Note that the value of this option is used as the
# *body* of the javascript function, a function definition
# with parameters named element and value will be generated for you
# for example:
# observe_field("glass", :frequency => 1, :function => "alert('Element changed')")
# will generate:
# new Form.Element.Observer('glass', 1, function(element, value) {alert('Element changed')})
# The element parameter is the DOM element being observed, and the value is its value at the
# time the observer is triggered.
#
# Additional options are:
# <tt>:frequency</tt>:: The frequency (in seconds) at which changes to
# this field will be detected. Not setting this
# option at all or to a value equal to or less than
# zero will use event based observation instead of
# time based observation.
# <tt>:update</tt>:: Specifies the DOM ID of the element whose
# innerHTML should be updated with the
# XMLHttpRequest response text.
# <tt>:with</tt>:: A JavaScript expression specifying the parameters
# for the XMLHttpRequest. The default is to send the
# key and value of the observed field. Any custom
# expressions should return a valid URL query string.
# The value of the field is stored in the JavaScript
# variable +value+.
#
# Examples
#
# :with => "'my_custom_key=' + value"
# :with => "'person[name]=' + prompt('New name')"
# :with => "Form.Element.serialize('other-field')"
#
# Finally
# :with => 'name'
# is shorthand for
# :with => "'name=' + value"
# This essentially just changes the key of the parameter.
#
# Additionally, you may specify any of the options documented in the
# <em>Common options</em> section at the top of this document.
#
# Example:
#
# # Sends params: {:title => 'Title of the book'} when the book_title input
# # field is changed.
# observe_field 'book_title',
# :url => 'http://example.com/books/edit/1',
# :with => 'title'
#
#
def observe_field(field_id, options = {})
if options[:frequency] && options[:frequency] > 0
build_observer('Form.Element.Observer', field_id, options)
else
build_observer('Form.Element.EventObserver', field_id, options)
end
end
# Observes the form with the DOM ID specified by +form_id+ and calls a
# callback when its contents have changed. The default callback is an
# Ajax call. By default all fields of the observed field are sent as
# parameters with the Ajax call.
#
# The +options+ for +observe_form+ are the same as the options for
# +observe_field+. The JavaScript variable +value+ available to the
# <tt>:with</tt> option is set to the serialized form by default.
def observe_form(form_id, options = {})
if options[:frequency]
build_observer('Form.Observer', form_id, options)
else
build_observer('Form.EventObserver', form_id, options)
end
end
# Periodically calls the specified url (<tt>options[:url]</tt>) every
# <tt>options[:frequency]</tt> seconds (default is 10). Usually used to
# update a specified div (<tt>options[:update]</tt>) with the results
# of the remote call. The options for specifying the target with <tt>:url</tt>
# and defining callbacks is the same as link_to_remote.
# Examples:
# # Call get_averages and put its results in 'avg' every 10 seconds
# # Generates:
# # new PeriodicalExecuter(function() {new Ajax.Updater('avg', '/grades/get_averages',
# # {asynchronous:true, evalScripts:true})}, 10)
# periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg')
#
# # Call invoice every 10 seconds with the id of the customer
# # If it succeeds, update the invoice DIV; if it fails, update the error DIV
# # Generates:
# # new PeriodicalExecuter(function() {new Ajax.Updater({success:'invoice',failure:'error'},
# # '/testing/invoice/16', {asynchronous:true, evalScripts:true})}, 10)
# periodically_call_remote(:url => { :action => 'invoice', :id => customer.id },
# :update => { :success => "invoice", :failure => "error" }
#
# # Call update every 20 seconds and update the new_block DIV
# # Generates:
# # new PeriodicalExecuter(function() {new Ajax.Updater('news_block', 'update', {asynchronous:true, evalScripts:true})}, 20)
# periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block')
#
def periodically_call_remote(options = {})
frequency = options[:frequency] || 10 # every ten seconds by default
code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})"
javascript_tag(code)
end
protected
def build_observer(klass, name, options = {})
if options[:with] && (options[:with] !~ /[\{=(.]/)
options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)"
else
options[:with] ||= 'value' unless options[:function]
end
callback = options[:function] || remote_function(options)
javascript = "new #{klass}('#{name}', "
javascript << "#{options[:frequency]}, " if options[:frequency]
javascript << "function(element, value) {"
javascript << "#{callback}}"
javascript << ")"
javascript_tag(javascript)
end
end
ActionController::Base.helper PrototypeHelper

View File

@ -1,297 +0,0 @@
if ENV['RAILS_ROOT']
environment = File.expand_path('vendor/gems/environment', ENV['RAILS_ROOT'])
require environment if File.exist?("#{environment}.rb")
end
$:.unshift File.expand_path('../../lib', __FILE__)
require 'test/unit'
require 'action_view'
require 'action_controller'
require 'active_model'
require 'prototype_helper'
class Bunny < Struct.new(:Bunny, :id)
end
class Author
extend ActiveModel::Naming
attr_reader :id
def save; @id = 1 end
def new_record?; @id.nil? end
def name
@id.nil? ? 'new author' : "author ##{@id}"
end
end
class Article
extend ActiveModel::Naming
attr_reader :id
attr_reader :author_id
def save; @id = 1; @author_id = 1 end
def new_record?; @id.nil? end
def name
@id.nil? ? 'new article' : "article ##{@id}"
end
end
class Author::Nested < Author; end
class PrototypeHelperTest < ActionView::TestCase
attr_accessor :formats, :output_buffer, :template_format
def _evaluate_assigns_and_ivars() end
def reset_formats(format)
@format = format
end
def setup
@record = @author = Author.new
@article = Article.new
super
@template = self
@controller = Class.new do
def url_for(options)
if options.is_a?(String)
options
else
url = "http://www.example.com/"
url << options[:action].to_s if options and options[:action]
url << "?a=#{options[:a]}" if options && options[:a]
url << "&b=#{options[:b]}" if options && options[:a] && options[:b]
url
end
end
end.new
end
def test_observe_form
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Observer('cart', 2, function(element, value) {new Ajax.Request('http://www.example.com/cart_changed', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
observe_form("cart", :frequency => 2, :url => { :action => "cart_changed" })
end
def test_observe_form_using_function_for_callback
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Observer('cart', 2, function(element, value) {alert('Form changed')})\n//]]>\n</script>),
observe_form("cart", :frequency => 2, :function => "alert('Form changed')")
end
def test_observe_field
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/reorder_if_empty', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
observe_field("glass", :frequency => 5.minutes, :url => { :action => "reorder_if_empty" })
end
def test_observe_field_using_with_option
expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(value)})})\n//]]>\n</script>)
assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id')
assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + encodeURIComponent(value)")
end
def test_observe_field_using_json_in_with_option
expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:{'id':value}})})\n//]]>\n</script>)
assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}")
end
def test_observe_field_using_function_for_callback
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {alert('Element changed')})\n//]]>\n</script>),
observe_field("glass", :frequency => 5.minutes, :function => "alert('Element changed')")
end
def test_observe_field_without_frequency
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.EventObserver('glass', function(element, value) {new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
observe_field("glass")
end
def test_periodically_call_remote
assert_dom_equal %(<script type="text/javascript">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Updater('schremser_bier', 'http://www.example.com/mehr_bier', {asynchronous:true, evalScripts:true})}, 10)\n//]]>\n</script>),
periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" })
end
def test_periodically_call_remote_with_frequency
assert_dom_equal(
"<script type=\"text/javascript\">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true})}, 2)\n//]]>\n</script>",
periodically_call_remote(:frequency => 2)
)
end
def test_form_remote_tag
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast })
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast })
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
form_remote_tag(:update => { :failure => "glass_of_water" }, :url => { :action => :fast })
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer',failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
form_remote_tag(:update => { :success => 'glass_of_beer', :failure => "glass_of_water" }, :url => { :action => :fast })
end
def test_form_remote_tag_with_method
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\"><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div>),
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :html => { :method => :put })
end
def test_form_remote_tag_with_block_in_erb
__in_erb_template = ''
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) { concat "Hello world!" }
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">Hello world!</form>), output_buffer
end
def test_on_callbacks
callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :success, :failure]
callbacks.each do |callback|
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();")
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({failure:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
form_remote_tag(:update => { :failure => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();")
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer',failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
form_remote_tag(:update => { :success => "glass_of_beer", :failure => "glass_of_water" }, :url => { :action => :fast }, callback=>"monkeys();")
end
#HTTP status codes 200 up to 599 have callbacks
#these should work
100.upto(599) do |callback|
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
end
#test 200 and 404
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on200:function(request){monkeys();}, on404:function(request){bananas();}, parameters:Form.serialize(this)}); return false;">),
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, 200=>"monkeys();", 404=>"bananas();")
#these shouldn't
1.upto(99) do |callback|
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">),
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
end
600.upto(999) do |callback|
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">),
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
end
#test ultimate combo
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on200:function(request){monkeys();}, on404:function(request){bananas();}, onComplete:function(request){c();}, onFailure:function(request){f();}, onLoading:function(request){c1()}, onSuccess:function(request){s()}, parameters:Form.serialize(this)}); return false;\">),
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :loading => "c1()", :success => "s()", :failure => "f();", :complete => "c();", 200=>"monkeys();", 404=>"bananas();")
end
def test_remote_form_for_with_record_identification_with_new_record
remote_form_for(@record, {:html => { :id => 'create-author' }}) {}
expected = %(<form action='#{authors_path}' onsubmit="new Ajax.Request('#{authors_path}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' id='create-author' method='post'></form>)
assert_dom_equal expected, output_buffer
end
def test_remote_form_for_with_record_identification_without_html_options
remote_form_for(@record) {}
expected = %(<form action='#{authors_path}' onsubmit="new Ajax.Request('#{authors_path}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' method='post' id='new_author'></form>)
assert_dom_equal expected, output_buffer
end
def test_remote_form_for_with_record_identification_with_existing_record
@record.save
remote_form_for(@record) {}
expected = %(<form action='#{author_path(@record)}' id='edit_author_1' method='post' onsubmit="new Ajax.Request('#{author_path(@record)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_author'><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div></form>)
assert_dom_equal expected, output_buffer
end
def test_remote_form_for_with_new_object_in_list
remote_form_for([@author, @article]) {}
expected = %(<form action='#{author_articles_path(@author)}' onsubmit="new Ajax.Request('#{author_articles_path(@author)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_article' method='post' id='new_article'></form>)
assert_dom_equal expected, output_buffer
end
def test_remote_form_for_with_existing_object_in_list
@author.save
@article.save
remote_form_for([@author, @article]) {}
expected = %(<form action='#{author_article_path(@author, @article)}' id='edit_article_1' method='post' onsubmit="new Ajax.Request('#{author_article_path(@author, @article)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_article'><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div></form>)
assert_dom_equal expected, output_buffer
end
def test_button_to_remote
assert_dom_equal %(<input class=\"fine\" type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true});\" />),
button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" })
assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.reponseText)}});\" />),
button_to_remote("Remote outpost", :complete => "alert(request.reponseText)", :url => { :action => "whatnot" })
assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.reponseText)}});\" />),
button_to_remote("Remote outpost", :success => "alert(request.reponseText)", :url => { :action => "whatnot" })
assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}});\" />),
button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot" })
assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&amp;b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}});\" />),
button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
end
def test_submit_to_remote
assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});\" type=\"button\" value=\"1000000\" />),
submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle")
end
def test_link_to_remote
assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }}, { :class => "fine" })
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", :complete => "alert(request.responseText)", :url => { :action => "whatnot" })
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", :success => "alert(request.responseText)", :url => { :action => "whatnot" })
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot" })
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&amp;b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:false, evalScripts:true}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :type => :synchronous)
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, insertion:'bottom'}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :position => :bottom)
end
def test_link_to_remote_html_options
assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :html => { :class => "fine" } })
end
def test_link_to_remote_url_quote_escaping
assert_dom_equal %(<a href="#" onclick="new Ajax.Request('http://www.example.com/whatnot\\\'s', {asynchronous:true, evalScripts:true}); return false;">Remote</a>),
link_to_remote("Remote", { :url => { :action => "whatnot's" } })
end
protected
def request_forgery_protection_token
nil
end
def protect_against_forgery?
false
end
def create_generator
block = Proc.new { |*args| yield *args if block_given? }
JavaScriptGenerator.new self, &block
end
def author_path(record)
"/authors/#{record.id}"
end
def authors_path
"/authors"
end
def author_articles_path(author)
"/authors/#{author.id}/articles"
end
def author_article_path(author, article)
"/authors/#{author.id}/articles/#{article.id}"
end
end

View File

@ -26,7 +26,7 @@ module Redmine
help_link = link_to(l(:setting_text_formatting), url,
:onclick => "window.open(\"#{ url }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
javascript_tag("var wikiToolbar = new jsToolBar($('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript help_link}'); wikiToolbar.draw();")
javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript help_link}'); wikiToolbar.draw();")
end
def initial_page_content(page)

View File

@ -2,69 +2,64 @@
Copyright (C) 2006-2012 Jean-Philippe Lang */
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;
}
if (checked) {
$('#'+id).find('input[type=checkbox]').attr('checked', true);
} else {
$('#'+id).find('input[type=checkbox]').removeAttr('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;
$(selector).each(function(index) {
if (!$(this).is(':checked')) { all_checked = false; }
});
$(selector).attr('checked', !all_checked)
}
function showAndScrollTo(id, focus) {
Element.show(id);
if (focus!=null) { Form.Element.focus(focus); }
Element.scrollTo(id);
$('#'+id).show();
if (focus!=null) {
$('#'+focus).focus();
}
$('html, body').animate({scrollTop: $('#'+id).offset().top}, 100);
}
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);
var tr = $(el).parents('tr').first();
var n = tr.next();
tr.toggleClass('open');
while (n.length && !n.hasClass('group')) {
n.toggle();
n = n.next('tr');
}
}
function collapseAllRowGroups(el) {
var tbody = Element.up(el, 'tbody');
tbody.childElements('tr').each(function(tr) {
if (tr.hasClassName('group')) {
tr.removeClassName('open');
var tbody = $(el).parents('tbody').first();
tbody.children('tr').each(function(index) {
if ($(this).hasClass('group')) {
$(this).removeClass('open');
} else {
tr.hide();
$(this).hide();
}
})
});
}
function expandAllRowGroups(el) {
var tbody = Element.up(el, 'tbody');
tbody.childElements('tr').each(function(tr) {
if (tr.hasClassName('group')) {
tr.addClassName('open');
var tbody = $(el).parents('tbody').first();
tbody.children('tr').each(function(index) {
if ($(this).hasClass('group')) {
$(this).addClass('open');
} else {
tr.show();
$(this).show();
}
})
});
}
function toggleAllRowGroups(el) {
var tr = Element.up(el, 'tr');
if (tr.hasClassName('open')) {
var tr = $(el).parents('tr').first();
if (tr.hasClass('open')) {
collapseAllRowGroups(el);
} else {
expandAllRowGroups(el);
@ -72,68 +67,72 @@ function toggleAllRowGroups(el) {
}
function toggleFieldset(el) {
var fieldset = Element.up(el, 'fieldset');
fieldset.toggleClassName('collapsed');
Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2});
var fieldset = $(el).parents('fieldset').first();
fieldset.toggleClass('collapsed');
fieldset.children('div').toggle();
}
function hideFieldset(el) {
var fieldset = Element.up(el, 'fieldset');
fieldset.toggleClassName('collapsed');
fieldset.down('div').hide();
var fieldset = $(el).parents('fieldset').first();
fieldset.toggleClass('collapsed');
fieldset.children('div').hide();
}
function add_filter() {
select = $('add_filter_select');
field = select.value
Element.show('tr_' + field);
check_box = $('cb_' + field);
check_box.checked = true;
var select = $('#add_filter_select');
var field = select.val();
$('#tr_'+field).show();
var check_box = $('#cb_' + field);
check_box.attr('checked', true);
toggle_filter(field);
select.selectedIndex = 0;
select.val('');
for (i=0; i<select.options.length; i++) {
if (select.options[i].value == field) {
select.options[i].disabled = true;
}
select.children('option').each(function(index) {
if ($(this).attr('value') == field) {
$(this).attr('disabled', true);
}
});
}
function toggle_filter(field) {
check_box = $('cb_' + field);
if (check_box.checked) {
Element.show("operators_" + field);
Form.Element.enable("operators_" + field);
check_box = $('#cb_' + field);
if (check_box.is(':checked')) {
$("#operators_" + field).show().removeAttr('disabled');
toggle_operator(field);
} else {
Element.hide("operators_" + field);
Form.Element.disable("operators_" + field);
$("#operators_" + field).hide().attr('disabled', true);
enableValues(field, []);
}
}
function enableValues(field, indexes) {
var f = $$(".values_" + field);
for(var i=0;i<f.length;i++) {
if (indexes.include(i)) {
Form.Element.enable(f[i]);
f[i].up('span').show();
$(".values_" + field).each(function(index) {
if (indexes.indexOf(index) >= 0) {
$(this).removeAttr('disabled');
$(this).parents('span').first().show();
} else {
f[i].value = '';
Form.Element.disable(f[i]);
f[i].up('span').hide();
$(this).val('');
$(this).attr('disabled', true);
$(this).parents('span').first().hide();
}
if ($(this).hasClass('group')) {
$(this).addClass('open');
} else {
$(this).show();
}
});
if (indexes.length > 0) {
Element.show("div_values_" + field);
$("#div_values_" + field).show();
} else {
Element.hide("div_values_" + field);
$("#div_values_" + field).hide();
}
}
function toggle_operator(field) {
operator = $("operators_" + field);
switch (operator.value) {
operator = $("#operators_" + field);
switch (operator.val()) {
case "!*":
case "*":
case "t":
@ -159,48 +158,45 @@ function toggle_operator(field) {
}
}
function toggle_multi_select(el) {
var select = $(el);
if (select.multiple == true) {
select.multiple = false;
function toggle_multi_select(id) {
var select = $('#'+id);
if (select.attr('multiple')) {
select.removeAttr('multiple');
} else {
select.multiple = true;
select.attr('multiple', true);
}
}
function submit_query_form(id) {
selectAllOptions("selected_columns");
$(id).submit();
$('#'+id).submit();
}
function apply_filters_observer() {
$$("#query_form input[type=text]").invoke("observe", "keypress", function(e){
if(e.keyCode == Event.KEY_RETURN) {
submit_query_form("query_form");
}
function observeIssueFilters() {
$('#query_form input[type=text]').keypress(function(e){
if (e.keyCode == 13) submit_query_form("query_form");
});
}
var fileFieldCount = 1;
function addFileField() {
var fields = $('attachments_fields');
if (fields.childElements().length >= 10) return false;
var fields = $('#attachments_fields');
if (fields.children().length >= 10) return false;
fileFieldCount++;
var s = new Element('span');
s.update(fields.down('span').innerHTML);
s.down('input.file').name = "attachments[" + fileFieldCount + "][file]";
s.down('input.description').name = "attachments[" + fileFieldCount + "][description]";
fields.appendChild(s);
var s = fields.children('span').first().clone();
s.children('input.file').attr('name', "attachments[" + fileFieldCount + "][file]").val('');
s.children('input.description').attr('name', "attachments[" + fileFieldCount + "][description]").val('');
fields.append(s);
}
function removeFileField(el) {
var fields = $('attachments_fields');
var s = Element.up(el, 'span');
if (fields.childElements().length > 1) {
var fields = $('#attachments_fields');
var s = $(el).parents('span').first();
if (fields.children().length > 1) {
s.remove();
} else {
s.update(s.innerHTML);
s.children('input.file').val('');
s.children('input.description').val('');
}
}
@ -217,182 +213,152 @@ function checkFileSize(el, maxSize, message) {
}
function showTab(name) {
var f = $$('div#content .tab-content');
for(var i=0; i<f.length; i++){
Element.hide(f[i]);
}
var f = $$('div.tabs a');
for(var i=0; i<f.length; i++){
Element.removeClassName(f[i], "selected");
}
Element.show('tab-content-' + name);
Element.addClassName('tab-' + name, "selected");
$('div#content .tab-content').hide();
$('div.tabs a').removeClass('selected');
$('#tab-content-' + name).show();
$('#tab-' + name).addClass('selected');
return false;
}
function moveTabRight(el) {
var lis = Element.up(el, 'div.tabs').down('ul').childElements();
var lis = $(el).parents('div.tabs').first().find('ul').children();
var tabsWidth = 0;
var i;
for (i=0; i<lis.length; i++) {
if (lis[i].visible()) {
tabsWidth += lis[i].getWidth() + 6;
var i = 0;
lis.each(function(){
if ($(this).is(':visible')) {
tabsWidth += $(this).width() + 6;
}
}
if (tabsWidth < Element.up(el, 'div.tabs').getWidth() - 60) {
return;
}
i=0;
while (i<lis.length && !lis[i].visible()) {
i++;
}
lis[i].hide();
});
if (tabsWidth < $(el).parents('div.tabs').first().width() - 60) { return; }
while (i<lis.length && !lis.eq(i).is(':visible')) { i++; }
lis.eq(i).hide();
}
function moveTabLeft(el) {
var lis = Element.up(el, 'div.tabs').down('ul').childElements();
var lis = $(el).parents('div.tabs').first().find('ul').children();
var i = 0;
while (i<lis.length && !lis[i].visible()) {
i++;
}
while (i<lis.length && !lis.eq(i).is(':visible')) { i++; }
if (i>0) {
lis[i-1].show();
lis.eq(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<lis.length; i++) {
if (lis[i].visible()) {
tabsWidth += lis[i].getWidth() + 6;
var el;
$('div.tabs').each(function() {
el = $(this);
lis = el.find('ul').children();
lis.each(function(){
if ($(this).is(':visible')) {
tabsWidth += $(this).width() + 6;
}
}
if ((tabsWidth < el.getWidth() - 60) && (lis[0].visible())) {
el.down('div.tabs-buttons').hide();
});
if ((tabsWidth < el.width() - 60) && (lis.first().is(':visible'))) {
el.find('div.tabs-buttons').hide();
} else {
el.down('div.tabs-buttons').show();
el.find('div.tabs-buttons').show();
}
});
}
function setPredecessorFieldsVisibility() {
relationType = $('relation_relation_type');
if (relationType && (relationType.value == "precedes" || relationType.value == "follows")) {
Element.show('predecessor_fields');
var relationType = $('#relation_relation_type');
if (relationType.val() == "precedes" || relationType.val() == "follows") {
$('#predecessor_fields').show();
} else {
Element.hide('predecessor_fields');
}
}
function promptToRemote(text, param, url) {
value = prompt(text + ':');
if (value) {
new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
return false;
$('#predecessor_fields').hide();
}
}
function showModal(id, width) {
el = $(id);
if (el == undefined || el.visible()) {return;}
var h = $$('body')[0].getHeight();
el = $('#'+id).first();
if (el.length == 0 || el.is(':visible')) {return;}
var h = $('body').height();
var d = document.createElement("div");
d.id = 'modalbg';
$('main').appendChild(d);
$('modalbg').setStyle({ width: '100%', height: h + 'px' });
$('modalbg').show();
$(d).appendTo('#main').css('width', '100%').css('height', h + 'px').show();
var pageWidth = document.viewport.getWidth();
var pageWidth = $(window).width();
if (width) {
el.setStyle({'width': width});
el.css('width', width);
}
el.setStyle({'left': (((pageWidth - el.getWidth())/2 *100) / pageWidth) + '%'});
el.addClassName('modal');
el.css('left', (((pageWidth - el.width())/2 *100) / pageWidth) + '%');
el.addClass('modal');
el.show();
if (el.down("input[type=text]")) {
el.down("input[type=text]").focus();
} else if (el.down("input[type=submit]")) {
el.down("input[type=submit]").focus();
}
el.find("input[type=text], input[type=submit]").first().focus();
}
function hideModal(el) {
var modal;
if (el) {
modal = Element.up(el, 'div.modal');
modal = $(el).parents('div.modal').first();
} else {
modal = $('ajax-modal');
modal = $('#ajax-modal');
}
if (modal) {
modal.hide();
}
var bg = $('modalbg');
if (bg) {
bg.remove();
}
$('#modalbg').remove();
}
function submitPreview(url, form, target) {
new Ajax.Updater(target, url, {
asynchronous:true,
evalScripts:true,
method:'post',
onComplete:function(request){Element.scrollTo(target)},
parameters:Form.serialize(form)
$.ajax({
url: url,
type: 'post',
data: $('#'+form).serialize(),
success: function(data){
$('#'+target).html(data);
$('html, body').animate({scrollTop: $('#'+target).offset().top}, 100);
}
});
}
function collapseScmEntry(id) {
var els = document.getElementsByClassName(id, 'browser');
for (var i = 0; i < els.length; i++) {
if (els[i].hasClassName('open')) {
collapseScmEntry(els[i].id);
$('.'+id).each(function() {
if ($(this).hasClass('open')) {
collapseScmEntry($(this).attr('id'));
}
Element.hide(els[i]);
}
$(id).removeClassName('open');
$(this).hide();
});
$('#'+id).removeClass('open');
}
function expandScmEntry(id) {
var els = document.getElementsByClassName(id, 'browser');
for (var i = 0; i < els.length; i++) {
Element.show(els[i]);
if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) {
expandScmEntry(els[i].id);
$('.'+id).each(function() {
$(this).show();
if ($(this).hasClass('loaded') && !$(this).hasClass('collapsed')) {
expandScmEntry($(this).attr('id'));
}
}
$(id).addClassName('open');
});
$('#'+id).addClass('open');
}
function scmEntryClick(id) {
el = $(id);
if (el.hasClassName('open')) {
function scmEntryClick(id, url) {
el = $('#'+id);
if (el.hasClass('open')) {
collapseScmEntry(id);
el.addClassName('collapsed');
el.addClass('collapsed');
return false;
} else if (el.hasClassName('loaded')) {
} else if (el.hasClass('loaded')) {
expandScmEntry(id);
el.removeClassName('collapsed');
el.removeClass('collapsed');
return false;
}
if (el.hasClassName('loading')) {
if (el.hasClass('loading')) {
return false;
}
el.addClassName('loading');
el.addClass('loading');
$.ajax({
url: url,
success: function(data){
el.after(data);
el.addClass('open').addClass('loaded').removeClass('loading');
}
});
return true;
}
function scmEntryLoaded(id) {
Element.addClassName(id, 'open');
Element.addClassName(id, 'loaded');
Element.removeClassName(id, 'loading');
}
function randomKey(size) {
var chars = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
var key = '';
@ -402,147 +368,139 @@ function randomKey(size) {
return key;
}
function observeParentIssueField(url) {
new Ajax.Autocompleter('issue_parent_issue_id',
'parent_issue_candidates',
url,
{ minChars: 3,
frequency: 0.5,
paramName: 'q',
method: 'get',
updateElement: function(value) {
document.getElementById('issue_parent_issue_id').value = value.id;
}});
}
function observeRelatedIssueField(url) {
new Ajax.Autocompleter('relation_issue_to_id',
'related_issue_candidates',
url,
{ minChars: 3,
frequency: 0.5,
paramName: 'q',
method: 'get',
updateElement: function(value) {
document.getElementById('relation_issue_to_id').value = value.id;
},
parameters: 'scope=all'
// Can't use Rails' remote select because we need the form data
function updateIssueFrom(url) {
$.ajax({
url: url,
type: 'post',
data: $('#issue-form').serialize()
});
}
function setVisible(id, visible) {
var el = $(id);
if (el) {if (visible) {el.show();} else {el.hide();}}
function updateBulkEditFrom(url) {
$.ajax({
url: url,
type: 'post',
data: $('#bulk_edit_form').serialize()
});
}
function observeAutocompleteField(fieldId, url) {
$('#'+fieldId).autocomplete({
source: url,
minLength: 2,
});
}
function observeSearchfield(fieldId, targetId, url) {
$('#'+fieldId).each(function() {
var $this = $(this);
$this.attr('data-value-was', $this.val());
var check = function() {
var val = $this.val();
if ($this.attr('data-value-was') != val){
$this.attr('data-value-was', val);
if (val != '') {
$.ajax({
url: url,
type: 'get',
data: {q: $this.val()},
success: function(data){ $('#'+targetId).html(data); },
beforeSend: function(){ $this.addClass('ajax-loading'); },
complete: function(){ $this.removeClass('ajax-loading'); }
});
}
}
};
var reset = function() {
if (timer) {
clearInterval(timer);
timer = setInterval(check, 300);
}
};
var timer = setInterval(check, 300);
$this.bind('keyup click mousemove', reset);
});
}
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);
if ($('#project_enabled_module_names_issue_tracking').attr('checked')) {
$('#project_trackers').show();
}else{
$('#project_trackers').hide();
}
};
Event.observe(window, 'load', f);
Event.observe('project_enabled_module_names_issue_tracking', 'change', f);
$(window).load(f);
$('#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(){
this.observedElements.each(function(el) {el.blur();})
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));
function initMyPageSortable(list, url) {
$('#list-'+list).sortable({
connectWith: '.block-receiver',
tolerance: 'pointer',
update: function(){
$.ajax({
url: url,
type: 'post',
data: {'blocks': $.map($('#list-'+list).children(), function(el){return $(el).attr('id');})}
});
}
});
/*
* 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;
$("#list-top, #list-left, #list-right").disableSelection();
}
if ($('ajax-indicator') && Ajax.activeRequestCount > 0 && $$('input.ajax-loading').size() == 0) {
Element.show('ajax-indicator');
}
},
onComplete: function(){
if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
Element.hide('ajax-indicator');
var warnLeavingUnsavedMessage;
function warnLeavingUnsaved(message) {
warnLeavingUnsavedMessage = message;
$('form').submit(function(){
$('textarea').removeData('changed');
});
$('textarea').change(function(){
$(this).data('changed', 'changed');
});
window.onbeforeunload = function(){
var warn = false;
$('textarea').blur().each(function(){
if ($(this).data('changed')) {
warn = true;
}
});
if (warn) {return warnLeavingUnsavedMessage;}
};
};
$(document).ready(function(){
$('#ajax-indicator').bind('ajaxSend', function(){
if ($('.ajax-loading').length == 0) {
$('#ajax-indicator').show();
}
});
$('#ajax-indicator').bind('ajaxStop', function(){
$('#ajax-indicator').hide();
});
});
function hideOnLoad() {
$$('.hol').each(function(el) {
el.hide();
});
$('.hol').hide();
}
function addFormObserversForDoubleSubmit() {
$$('form[method=post]').each(function(form) {
if (!form.hasClassName('multiple-submit')) {
form.on('submit', function(form_submission) {
if (form.getStorage().get('submitted')) {
form_submission.stop();
$('form[method=post]').each(function() {
if (!$(this).hasClass('multiple-submit')) {
$(this).submit(function(form_submission) {
if ($(form_submission.target).attr('data-submitted')) {
form_submission.preventDefault();
} else {
form.getStorage().set('submitted', true);
$(form_submission.target).attr('data-submitted', true);
}
});
}
});
}
Event.observe(window, 'load', hideOnLoad);
Event.observe(window, 'load', addFormObserversForDoubleSubmit);
$(document).ready(hideOnLoad);
$(document).ready(addFormObserversForDoubleSubmit);

View File

@ -1,108 +1,88 @@
/* Redmine - project management software
Copyright (C) 2006-2012 Jean-Philippe Lang */
var contextMenuObserving;
var contextMenuUrl;
var observingContextMenuClick;
ContextMenu = Class.create();
ContextMenu.prototype = {
initialize: function (url) {
this.url = url;
this.createMenu();
if (!observingContextMenuClick) {
Event.observe(document, 'click', this.Click.bindAsEventListener(this));
Event.observe(document, 'contextmenu', this.RightClick.bindAsEventListener(this));
observingContextMenuClick = true;
function contextMenuRightClick(event) {
var target = $(event.target);
if (target.is('a')) {return;}
var tr = target.parents('tr').first();
if (!tr.hasClass('hascontextmenu')) {return;}
event.preventDefault();
if (!contextMenuIsSelected(tr)) {
contextMenuUnselectAll();
contextMenuAddSelection(tr);
contextMenuSetLastSelected(tr);
}
contextMenuShow(event);
}
this.unselectAll();
this.lastSelected = null;
},
function contextMenuClick(event) {
var target = $(event.target);
var lastSelected;
RightClick: function(e) {
this.hideMenu();
// do not show the context menu on links
if (Event.element(e).tagName == 'A') { return; }
var tr = Event.findElement(e, 'tr');
if (tr == document || tr == undefined || !tr.hasClassName('hascontextmenu')) { return; }
Event.stop(e);
if (!this.isSelected(tr)) {
this.unselectAll();
this.addSelection(tr);
this.lastSelected = tr;
}
this.showMenu(e);
},
Click: function(e) {
if (Event.element(e).tagName == 'A' && Event.element(e).hasClassName('submenu')) {
Event.stop(e)
if (target.is('a') && target.hasClass('submenu')) {
event.preventDefault();
return;
}
this.hideMenu();
if (Event.element(e).tagName == 'A' || Event.element(e).tagName == 'IMG') { return; }
if (Event.isLeftClick(e) || (navigator.appVersion.match(/\bMSIE\b/))) {
var tr = Event.findElement(e, 'tr');
if (tr!=null && tr!=document && tr.hasClassName('hascontextmenu')) {
contextMenuHide();
if (target.is('a') || target.is('img')) { return; }
if (event.which == 1 /*TODO || (navigator.appVersion.match(/\bMSIE\b/))*/) {
var tr = target.parents('tr').first();
if (tr.length && tr.hasClass('hascontextmenu')) {
// a row was clicked, check if the click was on checkbox
var box = Event.findElement(e, 'input');
if (box!=document && box!=undefined) {
if (target.is('input')) {
// a checkbox may be clicked
if (box.checked) {
tr.addClassName('context-menu-selection');
if (target.attr('checked')) {
tr.addClass('context-menu-selection');
} else {
tr.removeClassName('context-menu-selection');
tr.removeClass('context-menu-selection');
}
} else {
if (e.ctrlKey || e.metaKey) {
this.toggleSelection(tr);
} else if (e.shiftKey) {
if (this.lastSelected != null) {
if (event.ctrlKey || event.metaKey) {
contextMenuToggleSelection(tr);
} else if (event.shiftKey) {
lastSelected = contextMenuLastSelected();
if (lastSelected.length) {
var toggling = false;
var rows = $$('.hascontextmenu');
for (i=0; i<rows.length; i++) {
if (toggling || rows[i]==tr) {
this.addSelection(rows[i]);
$('.hascontextmenu').each(function(){
if (toggling || $(this).is(tr)) {
contextMenuAddSelection($(this));
}
if (rows[i]==tr || rows[i]==this.lastSelected) {
if ($(this).is(tr) || $(this).is(lastSelected)) {
toggling = !toggling;
}
});
} else {
contextMenuAddSelection(tr);
}
} else {
this.addSelection(tr);
contextMenuUnselectAll();
contextMenuAddSelection(tr);
}
} else {
this.unselectAll();
this.addSelection(tr);
}
this.lastSelected = tr;
contextMenuSetLastSelected(tr);
}
} else {
// click is outside the rows
var t = Event.findElement(e, 'a');
if (t == document || t == undefined) {
this.unselectAll();
if (target.is('a') && (target.hasClass('disabled') || target.hasClass('submenu'))) {
event.preventDefault();
} else {
if (Element.hasClassName(t, 'disabled') || Element.hasClassName(t, 'submenu')) {
Event.stop(e);
contextMenuUnselectAll();
}
}
}
}
},
createMenu: function() {
if (!$('context-menu')) {
function contextMenuCreate() {
if ($('#context-menu').length < 1) {
var menu = document.createElement("div");
menu.setAttribute("id", "context-menu");
menu.setAttribute("style", "display:none;");
document.getElementById("content").appendChild(menu);
}
},
}
showMenu: function(e) {
var mouse_x = Event.pointerX(e);
var mouse_y = Event.pointerY(e);
function contextMenuShow(event) {
var mouse_x = event.pageX;
var mouse_y = event.pageY;
var render_x = mouse_x;
var render_y = mouse_y;
var dims;
@ -113,19 +93,17 @@ ContextMenu.prototype = {
var max_width;
var max_height;
$('context-menu').style['left'] = (render_x + 'px');
$('context-menu').style['top'] = (render_y + 'px');
Element.update('context-menu', '');
$('#context-menu').css('left', (render_x + 'px'));
$('#context-menu').css('top', (render_y + 'px'));
$('#context-menu').html('');
new Ajax.Updater({success:'context-menu'}, this.url,
{asynchronous:true,
method: 'get',
evalScripts:true,
parameters:Form.serialize(Event.findElement(e, 'form')),
onComplete:function(request){
dims = $('context-menu').getDimensions();
menu_width = dims.width;
menu_height = dims.height;
$.ajax({
url: contextMenuUrl,
data: $(event.target).parents('form').first().serialize(),
success: function(data, textStatus, jqXHR) {
$('#context-menu').html(data);
menu_width = $('#context-menu').width();
menu_height = $('#context-menu').height();
max_width = mouse_x + 2*menu_width;
max_height = mouse_y + menu_height;
@ -136,87 +114,109 @@ ContextMenu.prototype = {
/* display the menu above and/or to the left of the click if needed */
if (max_width > window_width) {
render_x -= menu_width;
$('context-menu').addClassName('reverse-x');
$('#context-menu').addClass('reverse-x');
} else {
$('context-menu').removeClassName('reverse-x');
$('#context-menu').removeClass('reverse-x');
}
if (max_height > window_height) {
render_y -= menu_height;
$('context-menu').addClassName('reverse-y');
$('#context-menu').addClass('reverse-y');
} else {
$('context-menu').removeClassName('reverse-y');
$('#context-menu').removeClass('reverse-y');
}
if (render_x <= 0) render_x = 1;
if (render_y <= 0) render_y = 1;
$('context-menu').style['left'] = (render_x + 'px');
$('context-menu').style['top'] = (render_y + 'px');
$('#context-menu').css('left', (render_x + 'px'));
$('#context-menu').css('top', (render_y + 'px'));
$('#context-menu').show();
Effect.Appear('context-menu', {duration: 0.20});
if (window.parseStylesheets) { window.parseStylesheets(); } // IE
}})
},
//if (window.parseStylesheets) { window.parseStylesheets(); } // IE
hideMenu: function() {
Element.hide('context-menu');
},
}
});
}
addSelection: function(tr) {
tr.addClassName('context-menu-selection');
this.checkSelectionBox(tr, true);
this.clearDocumentSelection();
},
function contextMenuSetLastSelected(tr) {
$('.cm-last').removeClass('cm-last');
tr.addClass('cm-last');
}
toggleSelection: function(tr) {
if (this.isSelected(tr)) {
this.removeSelection(tr);
function contextMenuLastSelected() {
return $('.cm-last').first();
}
function contextMenuUnselectAll() {
$('.hascontextmenu').each(function(){
contextMenuRemoveSelection($(this));
});
$('.cm-last').removeClass('cm-last');
}
function contextMenuHide() {
$('#context-menu').hide();
}
function contextMenuToggleSelection(tr) {
if (contextMenuIsSelected(tr)) {
contextMenuRemoveSelection(tr);
} else {
this.addSelection(tr);
contextMenuAddSelection(tr);
}
},
removeSelection: function(tr) {
tr.removeClassName('context-menu-selection');
this.checkSelectionBox(tr, false);
},
unselectAll: function() {
var rows = $$('.hascontextmenu');
for (i=0; i<rows.length; i++) {
this.removeSelection(rows[i]);
}
},
checkSelectionBox: function(tr, checked) {
var inputs = Element.getElementsBySelector(tr, 'input');
if (inputs.length > 0) { inputs[0].checked = checked; }
},
function contextMenuAddSelection(tr) {
tr.addClass('context-menu-selection');
contextMenuCheckSelectionBox(tr, true);
contextMenuClearDocumentSelection();
}
isSelected: function(tr) {
return Element.hasClassName(tr, 'context-menu-selection');
},
function contextMenuRemoveSelection(tr) {
tr.removeClass('context-menu-selection');
contextMenuCheckSelectionBox(tr, false);
}
clearDocumentSelection: function() {
function contextMenuIsSelected(tr) {
return tr.hasClass('context-menu-selection');
}
function contextMenuCheckSelectionBox(tr, checked) {
tr.find('input[type=checkbox]').attr('checked', checked);
}
function contextMenuClearDocumentSelection() {
// TODO
if (document.selection) {
document.selection.clear(); // IE
} else {
window.getSelection().removeAllRanges();
}
}
function contextMenuInit(url) {
contextMenuUrl = url;
contextMenuCreate();
contextMenuUnselectAll();
if (!contextMenuObserving) {
$(document).click(contextMenuClick);
$(document).contextmenu(contextMenuRightClick);
contextMenuObserving = true;
}
}
function toggleIssuesSelection(el) {
var boxes = el.getElementsBySelector('input[type=checkbox]');
var boxes = $(el).parents('form').find('input[type=checkbox]');
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.each(function(){ if (!$(this).attr('checked')) { all_checked = false; } });
boxes.each(function(){
if (all_checked) {
boxes[i].checked = false;
boxes[i].up('tr').removeClassName('context-menu-selection');
} else if (boxes[i].checked == false) {
boxes[i].checked = true;
boxes[i].up('tr').addClassName('context-menu-selection');
}
$(this).removeAttr('checked');
$(this).parents('tr').removeClass('context-menu-selection');
} else if (!$(this).attr('checked')) {
$(this).attr('checked', true);
$(this).parents('tr').addClass('context-menu-selection');
}
});
}
function window_size() {

View File

@ -1,963 +0,0 @@
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
// (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
// Contributors:
// Richard Livsey
// Rahul Bhargava
// Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/
// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.
if(typeof Effect == 'undefined')
throw("controls.js requires including script.aculo.us' effects.js library");
var Autocompleter = { };
Autocompleter.Base = Class.create({
baseInitialize: function(element, update, options) {
element = $(element);
this.element = element;
this.update = $(update);
this.hasFocus = false;
this.changed = false;
this.active = false;
this.index = 0;
this.entryCount = 0;
this.oldElementValue = this.element.value;
if(this.setOptions)
this.setOptions(options);
else
this.options = options || { };
this.options.paramName = this.options.paramName || this.element.name;
this.options.tokens = this.options.tokens || [];
this.options.frequency = this.options.frequency || 0.4;
this.options.minChars = this.options.minChars || 1;
this.options.onShow = this.options.onShow ||
function(element, update){
if(!update.style.position || update.style.position=='absolute') {
update.style.position = 'absolute';
Position.clone(element, update, {
setHeight: false,
offsetTop: element.offsetHeight
});
}
Effect.Appear(update,{duration:0.15});
};
this.options.onHide = this.options.onHide ||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
if(typeof(this.options.tokens) == 'string')
this.options.tokens = new Array(this.options.tokens);
// Force carriage returns as token delimiters anyway
if (!this.options.tokens.include('\n'))
this.options.tokens.push('\n');
this.observer = null;
this.element.setAttribute('autocomplete','off');
Element.hide(this.update);
Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
},
show: function() {
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
if(!this.iefix &&
(Prototype.Browser.IE) &&
(Element.getStyle(this.update, 'position')=='absolute')) {
new Insertion.After(this.update,
'<iframe id="' + this.update.id + '_iefix" '+
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
this.iefix = $(this.update.id+'_iefix');
}
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
},
fixIEOverlapping: function() {
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
this.iefix.style.zIndex = 1;
this.update.style.zIndex = 2;
Element.show(this.iefix);
},
hide: function() {
this.stopIndicator();
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
if(this.iefix) Element.hide(this.iefix);
},
startIndicator: function() {
if(this.options.indicator) Element.show(this.options.indicator);
},
stopIndicator: function() {
if(this.options.indicator) Element.hide(this.options.indicator);
},
onKeyPress: function(event) {
if(this.active)
switch(event.keyCode) {
case Event.KEY_TAB:
case Event.KEY_RETURN:
this.selectEntry();
Event.stop(event);
case Event.KEY_ESC:
this.hide();
this.active = false;
Event.stop(event);
return;
case Event.KEY_LEFT:
case Event.KEY_RIGHT:
return;
case Event.KEY_UP:
this.markPrevious();
this.render();
Event.stop(event);
return;
case Event.KEY_DOWN:
this.markNext();
this.render();
Event.stop(event);
return;
}
else
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
(Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
this.changed = true;
this.hasFocus = true;
if(this.observer) clearTimeout(this.observer);
this.observer =
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
},
activate: function() {
this.changed = false;
this.hasFocus = true;
this.getUpdatedChoices();
},
onHover: function(event) {
var element = Event.findElement(event, 'LI');
if(this.index != element.autocompleteIndex)
{
this.index = element.autocompleteIndex;
this.render();
}
Event.stop(event);
},
onClick: function(event) {
var element = Event.findElement(event, 'LI');
this.index = element.autocompleteIndex;
this.selectEntry();
this.hide();
},
onBlur: function(event) {
// needed to make click events working
setTimeout(this.hide.bind(this), 250);
this.hasFocus = false;
this.active = false;
},
render: function() {
if(this.entryCount > 0) {
for (var i = 0; i < this.entryCount; i++)
this.index==i ?
Element.addClassName(this.getEntry(i),"selected") :
Element.removeClassName(this.getEntry(i),"selected");
if(this.hasFocus) {
this.show();
this.active = true;
}
} else {
this.active = false;
this.hide();
}
},
markPrevious: function() {
if(this.index > 0) this.index--;
else this.index = this.entryCount-1;
this.getEntry(this.index).scrollIntoView(true);
},
markNext: function() {
if(this.index < this.entryCount-1) this.index++;
else this.index = 0;
this.getEntry(this.index).scrollIntoView(false);
},
getEntry: function(index) {
return this.update.firstChild.childNodes[index];
},
getCurrentEntry: function() {
return this.getEntry(this.index);
},
selectEntry: function() {
this.active = false;
this.updateElement(this.getCurrentEntry());
},
updateElement: function(selectedElement) {
if (this.options.updateElement) {
this.options.updateElement(selectedElement);
return;
}
var value = '';
if (this.options.select) {
var nodes = $(selectedElement).select('.' + this.options.select) || [];
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
} else
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
var bounds = this.getTokenBounds();
if (bounds[0] != -1) {
var newValue = this.element.value.substr(0, bounds[0]);
var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
if (whitespace)
newValue += whitespace[0];
this.element.value = newValue + value + this.element.value.substr(bounds[1]);
} else {
this.element.value = value;
}
this.oldElementValue = this.element.value;
this.element.focus();
if (this.options.afterUpdateElement)
this.options.afterUpdateElement(this.element, selectedElement);
},
updateChoices: function(choices) {
if(!this.changed && this.hasFocus) {
this.update.innerHTML = choices;
Element.cleanWhitespace(this.update);
Element.cleanWhitespace(this.update.down());
if(this.update.firstChild && this.update.down().childNodes) {
this.entryCount =
this.update.down().childNodes.length;
for (var i = 0; i < this.entryCount; i++) {
var entry = this.getEntry(i);
entry.autocompleteIndex = i;
this.addObservers(entry);
}
} else {
this.entryCount = 0;
}
this.stopIndicator();
this.index = 0;
if(this.entryCount==1 && this.options.autoSelect) {
this.selectEntry();
this.hide();
} else {
this.render();
}
}
},
addObservers: function(element) {
Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
Event.observe(element, "click", this.onClick.bindAsEventListener(this));
},
onObserverEvent: function() {
this.changed = false;
this.tokenBounds = null;
if(this.getToken().length>=this.options.minChars) {
this.getUpdatedChoices();
} else {
this.active = false;
this.hide();
}
this.oldElementValue = this.element.value;
},
getToken: function() {
var bounds = this.getTokenBounds();
return this.element.value.substring(bounds[0], bounds[1]).strip();
},
getTokenBounds: function() {
if (null != this.tokenBounds) return this.tokenBounds;
var value = this.element.value;
if (value.strip().empty()) return [-1, 0];
var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
var offset = (diff == this.oldElementValue.length ? 1 : 0);
var prevTokenPos = -1, nextTokenPos = value.length;
var tp;
for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
if (tp > prevTokenPos) prevTokenPos = tp;
tp = value.indexOf(this.options.tokens[index], diff + offset);
if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
}
return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
}
});
Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
var boundary = Math.min(newS.length, oldS.length);
for (var index = 0; index < boundary; ++index)
if (newS[index] != oldS[index])
return index;
return boundary;
};
Ajax.Autocompleter = Class.create(Autocompleter.Base, {
initialize: function(element, update, url, options) {
this.baseInitialize(element, update, options);
this.options.asynchronous = true;
this.options.onComplete = this.onComplete.bind(this);
this.options.defaultParams = this.options.parameters || null;
this.url = url;
},
getUpdatedChoices: function() {
this.startIndicator();
var entry = encodeURIComponent(this.options.paramName) + '=' +
encodeURIComponent(this.getToken());
this.options.parameters = this.options.callback ?
this.options.callback(this.element, entry) : entry;
if(this.options.defaultParams)
this.options.parameters += '&' + this.options.defaultParams;
new Ajax.Request(this.url, this.options);
},
onComplete: function(request) {
this.updateChoices(request.responseText);
}
});
// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
// text only at the beginning of strings in the
// autocomplete array. Defaults to true, which will
// match text at the beginning of any *word* in the
// strings in the autocomplete array. If you want to
// search anywhere in the string, additionally set
// the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
// a partial match (unlike minChars, which defines
// how many characters are required to do any match
// at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
// Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.
Autocompleter.Local = Class.create(Autocompleter.Base, {
initialize: function(element, update, array, options) {
this.baseInitialize(element, update, options);
this.options.array = array;
},
getUpdatedChoices: function() {
this.updateChoices(this.options.selector(this));
},
setOptions: function(options) {
this.options = Object.extend({
choices: 10,
partialSearch: true,
partialChars: 2,
ignoreCase: true,
fullSearch: false,
selector: function(instance) {
var ret = []; // Beginning matches
var partial = []; // Inside matches
var entry = instance.getToken();
var count = 0;
for (var i = 0; i < instance.options.array.length &&
ret.length < instance.options.choices ; i++) {
var elem = instance.options.array[i];
var foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase()) :
elem.indexOf(entry);
while (foundPos != -1) {
if (foundPos == 0 && elem.length != entry.length) {
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
elem.substr(entry.length) + "</li>");
break;
} else if (entry.length >= instance.options.partialChars &&
instance.options.partialSearch && foundPos != -1) {
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
foundPos + entry.length) + "</li>");
break;
}
}
foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
elem.indexOf(entry, foundPos + 1);
}
}
if (partial.length)
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
return "<ul>" + ret.join('') + "</ul>";
}
}, options || { });
}
});
// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
setTimeout(function() {
Field.activate(field);
}, 1);
};
Ajax.InPlaceEditor = Class.create({
initialize: function(element, url, options) {
this.url = url;
this.element = element = $(element);
this.prepareOptions();
this._controls = { };
arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
Object.extend(this.options, options || { });
if (!this.options.formId && this.element.id) {
this.options.formId = this.element.id + '-inplaceeditor';
if ($(this.options.formId))
this.options.formId = '';
}
if (this.options.externalControl)
this.options.externalControl = $(this.options.externalControl);
if (!this.options.externalControl)
this.options.externalControlOnly = false;
this._originalBackground = this.element.getStyle('background-color') || 'transparent';
this.element.title = this.options.clickToEditText;
this._boundCancelHandler = this.handleFormCancellation.bind(this);
this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
this._boundFailureHandler = this.handleAJAXFailure.bind(this);
this._boundSubmitHandler = this.handleFormSubmission.bind(this);
this._boundWrapperHandler = this.wrapUp.bind(this);
this.registerListeners();
},
checkForEscapeOrReturn: function(e) {
if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
if (Event.KEY_ESC == e.keyCode)
this.handleFormCancellation(e);
else if (Event.KEY_RETURN == e.keyCode)
this.handleFormSubmission(e);
},
createControl: function(mode, handler, extraClasses) {
var control = this.options[mode + 'Control'];
var text = this.options[mode + 'Text'];
if ('button' == control) {
var btn = document.createElement('input');
btn.type = 'submit';
btn.value = text;
btn.className = 'editor_' + mode + '_button';
if ('cancel' == mode)
btn.onclick = this._boundCancelHandler;
this._form.appendChild(btn);
this._controls[mode] = btn;
} else if ('link' == control) {
var link = document.createElement('a');
link.href = '#';
link.appendChild(document.createTextNode(text));
link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
link.className = 'editor_' + mode + '_link';
if (extraClasses)
link.className += ' ' + extraClasses;
this._form.appendChild(link);
this._controls[mode] = link;
}
},
createEditField: function() {
var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
var fld;
if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
fld = document.createElement('input');
fld.type = 'text';
var size = this.options.size || this.options.cols || 0;
if (0 < size) fld.size = size;
} else {
fld = document.createElement('textarea');
fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
fld.cols = this.options.cols || 40;
}
fld.name = this.options.paramName;
fld.value = text; // No HTML breaks conversion anymore
fld.className = 'editor_field';
if (this.options.submitOnBlur)
fld.onblur = this._boundSubmitHandler;
this._controls.editor = fld;
if (this.options.loadTextURL)
this.loadExternalText();
this._form.appendChild(this._controls.editor);
},
createForm: function() {
var ipe = this;
function addText(mode, condition) {
var text = ipe.options['text' + mode + 'Controls'];
if (!text || condition === false) return;
ipe._form.appendChild(document.createTextNode(text));
};
this._form = $(document.createElement('form'));
this._form.id = this.options.formId;
this._form.addClassName(this.options.formClassName);
this._form.onsubmit = this._boundSubmitHandler;
this.createEditField();
if ('textarea' == this._controls.editor.tagName.toLowerCase())
this._form.appendChild(document.createElement('br'));
if (this.options.onFormCustomization)
this.options.onFormCustomization(this, this._form);
addText('Before', this.options.okControl || this.options.cancelControl);
this.createControl('ok', this._boundSubmitHandler);
addText('Between', this.options.okControl && this.options.cancelControl);
this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
addText('After', this.options.okControl || this.options.cancelControl);
},
destroy: function() {
if (this._oldInnerHTML)
this.element.innerHTML = this._oldInnerHTML;
this.leaveEditMode();
this.unregisterListeners();
},
enterEditMode: function(e) {
if (this._saving || this._editing) return;
this._editing = true;
this.triggerCallback('onEnterEditMode');
if (this.options.externalControl)
this.options.externalControl.hide();
this.element.hide();
this.createForm();
this.element.parentNode.insertBefore(this._form, this.element);
if (!this.options.loadTextURL)
this.postProcessEditField();
if (e) Event.stop(e);
},
enterHover: function(e) {
if (this.options.hoverClassName)
this.element.addClassName(this.options.hoverClassName);
if (this._saving) return;
this.triggerCallback('onEnterHover');
},
getText: function() {
return this.element.innerHTML.unescapeHTML();
},
handleAJAXFailure: function(transport) {
this.triggerCallback('onFailure', transport);
if (this._oldInnerHTML) {
this.element.innerHTML = this._oldInnerHTML;
this._oldInnerHTML = null;
}
},
handleFormCancellation: function(e) {
this.wrapUp();
if (e) Event.stop(e);
},
handleFormSubmission: function(e) {
var form = this._form;
var value = $F(this._controls.editor);
this.prepareSubmission();
var params = this.options.callback(form, value) || '';
if (Object.isString(params))
params = params.toQueryParams();
params.editorId = this.element.id;
if (this.options.htmlResponse) {
var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
Object.extend(options, {
parameters: params,
onComplete: this._boundWrapperHandler,
onFailure: this._boundFailureHandler
});
new Ajax.Updater({ success: this.element }, this.url, options);
} else {
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
Object.extend(options, {
parameters: params,
onComplete: this._boundWrapperHandler,
onFailure: this._boundFailureHandler
});
new Ajax.Request(this.url, options);
}
if (e) Event.stop(e);
},
leaveEditMode: function() {
this.element.removeClassName(this.options.savingClassName);
this.removeForm();
this.leaveHover();
this.element.style.backgroundColor = this._originalBackground;
this.element.show();
if (this.options.externalControl)
this.options.externalControl.show();
this._saving = false;
this._editing = false;
this._oldInnerHTML = null;
this.triggerCallback('onLeaveEditMode');
},
leaveHover: function(e) {
if (this.options.hoverClassName)
this.element.removeClassName(this.options.hoverClassName);
if (this._saving) return;
this.triggerCallback('onLeaveHover');
},
loadExternalText: function() {
this._form.addClassName(this.options.loadingClassName);
this._controls.editor.disabled = true;
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
Object.extend(options, {
parameters: 'editorId=' + encodeURIComponent(this.element.id),
onComplete: Prototype.emptyFunction,
onSuccess: function(transport) {
this._form.removeClassName(this.options.loadingClassName);
var text = transport.responseText;
if (this.options.stripLoadedTextTags)
text = text.stripTags();
this._controls.editor.value = text;
this._controls.editor.disabled = false;
this.postProcessEditField();
}.bind(this),
onFailure: this._boundFailureHandler
});
new Ajax.Request(this.options.loadTextURL, options);
},
postProcessEditField: function() {
var fpc = this.options.fieldPostCreation;
if (fpc)
$(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
},
prepareOptions: function() {
this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
[this._extraDefaultOptions].flatten().compact().each(function(defs) {
Object.extend(this.options, defs);
}.bind(this));
},
prepareSubmission: function() {
this._saving = true;
this.removeForm();
this.leaveHover();
this.showSaving();
},
registerListeners: function() {
this._listeners = { };
var listener;
$H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
listener = this[pair.value].bind(this);
this._listeners[pair.key] = listener;
if (!this.options.externalControlOnly)
this.element.observe(pair.key, listener);
if (this.options.externalControl)
this.options.externalControl.observe(pair.key, listener);
}.bind(this));
},
removeForm: function() {
if (!this._form) return;
this._form.remove();
this._form = null;
this._controls = { };
},
showSaving: function() {
this._oldInnerHTML = this.element.innerHTML;
this.element.innerHTML = this.options.savingText;
this.element.addClassName(this.options.savingClassName);
this.element.style.backgroundColor = this._originalBackground;
this.element.show();
},
triggerCallback: function(cbName, arg) {
if ('function' == typeof this.options[cbName]) {
this.options[cbName](this, arg);
}
},
unregisterListeners: function() {
$H(this._listeners).each(function(pair) {
if (!this.options.externalControlOnly)
this.element.stopObserving(pair.key, pair.value);
if (this.options.externalControl)
this.options.externalControl.stopObserving(pair.key, pair.value);
}.bind(this));
},
wrapUp: function(transport) {
this.leaveEditMode();
// Can't use triggerCallback due to backward compatibility: requires
// binding + direct element
this._boundComplete(transport, this.element);
}
});
Object.extend(Ajax.InPlaceEditor.prototype, {
dispose: Ajax.InPlaceEditor.prototype.destroy
});
Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
initialize: function($super, element, url, options) {
this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
$super(element, url, options);
},
createEditField: function() {
var list = document.createElement('select');
list.name = this.options.paramName;
list.size = 1;
this._controls.editor = list;
this._collection = this.options.collection || [];
if (this.options.loadCollectionURL)
this.loadCollection();
else
this.checkForExternalText();
this._form.appendChild(this._controls.editor);
},
loadCollection: function() {
this._form.addClassName(this.options.loadingClassName);
this.showLoadingText(this.options.loadingCollectionText);
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
Object.extend(options, {
parameters: 'editorId=' + encodeURIComponent(this.element.id),
onComplete: Prototype.emptyFunction,
onSuccess: function(transport) {
var js = transport.responseText.strip();
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
throw('Server returned an invalid collection representation.');
this._collection = eval(js);
this.checkForExternalText();
}.bind(this),
onFailure: this.onFailure
});
new Ajax.Request(this.options.loadCollectionURL, options);
},
showLoadingText: function(text) {
this._controls.editor.disabled = true;
var tempOption = this._controls.editor.firstChild;
if (!tempOption) {
tempOption = document.createElement('option');
tempOption.value = '';
this._controls.editor.appendChild(tempOption);
tempOption.selected = true;
}
tempOption.update((text || '').stripScripts().stripTags());
},
checkForExternalText: function() {
this._text = this.getText();
if (this.options.loadTextURL)
this.loadExternalText();
else
this.buildOptionList();
},
loadExternalText: function() {
this.showLoadingText(this.options.loadingText);
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
Object.extend(options, {
parameters: 'editorId=' + encodeURIComponent(this.element.id),
onComplete: Prototype.emptyFunction,
onSuccess: function(transport) {
this._text = transport.responseText.strip();
this.buildOptionList();
}.bind(this),
onFailure: this.onFailure
});
new Ajax.Request(this.options.loadTextURL, options);
},
buildOptionList: function() {
this._form.removeClassName(this.options.loadingClassName);
this._collection = this._collection.map(function(entry) {
return 2 === entry.length ? entry : [entry, entry].flatten();
});
var marker = ('value' in this.options) ? this.options.value : this._text;
var textFound = this._collection.any(function(entry) {
return entry[0] == marker;
}.bind(this));
this._controls.editor.update('');
var option;
this._collection.each(function(entry, index) {
option = document.createElement('option');
option.value = entry[0];
option.selected = textFound ? entry[0] == marker : 0 == index;
option.appendChild(document.createTextNode(entry[1]));
this._controls.editor.appendChild(option);
}.bind(this));
this._controls.editor.disabled = false;
Field.scrollFreeActivate(this._controls.editor);
}
});
//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only exists for a while, in order to let ****
//**** users adapt to the new API. Read up on the new ****
//**** API and convert your code to it ASAP! ****
Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
if (!options) return;
function fallback(name, expr) {
if (name in options || expr === undefined) return;
options[name] = expr;
};
fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
options.cancelLink == options.cancelButton == false ? false : undefined)));
fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
options.okLink == options.okButton == false ? false : undefined)));
fallback('highlightColor', options.highlightcolor);
fallback('highlightEndColor', options.highlightendcolor);
};
Object.extend(Ajax.InPlaceEditor, {
DefaultOptions: {
ajaxOptions: { },
autoRows: 3, // Use when multi-line w/ rows == 1
cancelControl: 'link', // 'link'|'button'|false
cancelText: 'cancel',
clickToEditText: 'Click to edit',
externalControl: null, // id|elt
externalControlOnly: false,
fieldPostCreation: 'activate', // 'activate'|'focus'|false
formClassName: 'inplaceeditor-form',
formId: null, // id|elt
highlightColor: '#ffff99',
highlightEndColor: '#ffffff',
hoverClassName: '',
htmlResponse: true,
loadingClassName: 'inplaceeditor-loading',
loadingText: 'Loading...',
okControl: 'button', // 'link'|'button'|false
okText: 'ok',
paramName: 'value',
rows: 1, // If 1 and multi-line, uses autoRows
savingClassName: 'inplaceeditor-saving',
savingText: 'Saving...',
size: 0,
stripLoadedTextTags: false,
submitOnBlur: false,
textAfterControls: '',
textBeforeControls: '',
textBetweenControls: ''
},
DefaultCallbacks: {
callback: function(form) {
return Form.serialize(form);
},
onComplete: function(transport, element) {
// For backward compatibility, this one is bound to the IPE, and passes
// the element directly. It was too often customized, so we don't break it.
new Effect.Highlight(element, {
startcolor: this.options.highlightColor, keepBackgroundImage: true });
},
onEnterEditMode: null,
onEnterHover: function(ipe) {
ipe.element.style.backgroundColor = ipe.options.highlightColor;
if (ipe._effect)
ipe._effect.cancel();
},
onFailure: function(transport, ipe) {
alert('Error communication with the server: ' + transport.responseText.stripTags());
},
onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
onLeaveEditMode: null,
onLeaveHover: function(ipe) {
ipe._effect = new Effect.Highlight(ipe.element, {
startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
restorecolor: ipe._originalBackground, keepBackgroundImage: true
});
}
},
Listeners: {
click: 'enterEditMode',
keydown: 'checkForEscapeOrReturn',
mouseover: 'enterHover',
mouseout: 'leaveHover'
}
});
Ajax.InPlaceCollectionEditor.DefaultOptions = {
loadingCollectionText: 'Loading options...'
};
// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields
Form.Element.DelayedObserver = Class.create({
initialize: function(element, delay, callback) {
this.delay = delay || 0.5;
this.element = $(element);
this.callback = callback;
this.timer = null;
this.lastValue = $F(this.element);
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
},
delayedListener: function(event) {
if(this.lastValue == $F(this.element)) return;
if(this.timer) clearTimeout(this.timer);
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
this.lastValue = $F(this.element);
},
onTimerEvent: function() {
this.timer = null;
this.callback(this.element, $F(this.element));
}
});

View File

@ -1,973 +0,0 @@
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/
if(Object.isUndefined(Effect))
throw("dragdrop.js requires including script.aculo.us' effects.js library");
var Droppables = {
drops: [],
remove: function(element) {
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
},
add: function(element) {
element = $(element);
var options = Object.extend({
greedy: true,
hoverclass: null,
tree: false
}, arguments[1] || { });
// cache containers
if(options.containment) {
options._containers = [];
var containment = options.containment;
if(Object.isArray(containment)) {
containment.each( function(c) { options._containers.push($(c)) });
} else {
options._containers.push($(containment));
}
}
if(options.accept) options.accept = [options.accept].flatten();
Element.makePositioned(element); // fix IE
options.element = element;
this.drops.push(options);
},
findDeepestChild: function(drops) {
deepest = drops[0];
for (i = 1; i < drops.length; ++i)
if (Element.isParent(drops[i].element, deepest.element))
deepest = drops[i];
return deepest;
},
isContained: function(element, drop) {
var containmentNode;
if(drop.tree) {
containmentNode = element.treeNode;
} else {
containmentNode = element.parentNode;
}
return drop._containers.detect(function(c) { return containmentNode == c });
},
isAffected: function(point, element, drop) {
return (
(drop.element!=element) &&
((!drop._containers) ||
this.isContained(element, drop)) &&
((!drop.accept) ||
(Element.classNames(element).detect(
function(v) { return drop.accept.include(v) } ) )) &&
Position.within(drop.element, point[0], point[1]) );
},
deactivate: function(drop) {
if(drop.hoverclass)
Element.removeClassName(drop.element, drop.hoverclass);
this.last_active = null;
},
activate: function(drop) {
if(drop.hoverclass)
Element.addClassName(drop.element, drop.hoverclass);
this.last_active = drop;
},
show: function(point, element) {
if(!this.drops.length) return;
var drop, affected = [];
this.drops.each( function(drop) {
if(Droppables.isAffected(point, element, drop))
affected.push(drop);
});
if(affected.length>0)
drop = Droppables.findDeepestChild(affected);
if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
if (drop) {
Position.within(drop.element, point[0], point[1]);
if(drop.onHover)
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
if (drop != this.last_active) Droppables.activate(drop);
}
},
fire: function(event, element) {
if(!this.last_active) return;
Position.prepare();
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
if (this.last_active.onDrop) {
this.last_active.onDrop(element, this.last_active.element, event);
return true;
}
},
reset: function() {
if(this.last_active)
this.deactivate(this.last_active);
}
};
var Draggables = {
drags: [],
observers: [],
register: function(draggable) {
if(this.drags.length == 0) {
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
}
this.drags.push(draggable);
},
unregister: function(draggable) {
this.drags = this.drags.reject(function(d) { return d==draggable });
if(this.drags.length == 0) {
Event.stopObserving(document, "mouseup", this.eventMouseUp);
Event.stopObserving(document, "mousemove", this.eventMouseMove);
Event.stopObserving(document, "keypress", this.eventKeypress);
}
},
activate: function(draggable) {
if(draggable.options.delay) {
this._timeout = setTimeout(function() {
Draggables._timeout = null;
window.focus();
Draggables.activeDraggable = draggable;
}.bind(this), draggable.options.delay);
} else {
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
this.activeDraggable = draggable;
}
},
deactivate: function() {
this.activeDraggable = null;
},
updateDrag: function(event) {
if(!this.activeDraggable) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
// Mozilla-based browsers fire successive mousemove events with
// the same coordinates, prevent needless redrawing (moz bug?)
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
this._lastPointer = pointer;
this.activeDraggable.updateDrag(event, pointer);
},
endDrag: function(event) {
if(this._timeout) {
clearTimeout(this._timeout);
this._timeout = null;
}
if(!this.activeDraggable) return;
this._lastPointer = null;
this.activeDraggable.endDrag(event);
this.activeDraggable = null;
},
keyPress: function(event) {
if(this.activeDraggable)
this.activeDraggable.keyPress(event);
},
addObserver: function(observer) {
this.observers.push(observer);
this._cacheObserverCallbacks();
},
removeObserver: function(element) { // element instead of observer fixes mem leaks
this.observers = this.observers.reject( function(o) { return o.element==element });
this._cacheObserverCallbacks();
},
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
if(this[eventName+'Count'] > 0)
this.observers.each( function(o) {
if(o[eventName]) o[eventName](eventName, draggable, event);
});
if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
},
_cacheObserverCallbacks: function() {
['onStart','onEnd','onDrag'].each( function(eventName) {
Draggables[eventName+'Count'] = Draggables.observers.select(
function(o) { return o[eventName]; }
).length;
});
}
};
/*--------------------------------------------------------------------------*/
var Draggable = Class.create({
initialize: function(element) {
var defaults = {
handle: false,
reverteffect: function(element, top_offset, left_offset) {
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
queue: {scope:'_draggable', position:'end'}
});
},
endeffect: function(element) {
var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
queue: {scope:'_draggable', position:'end'},
afterFinish: function(){
Draggable._dragging[element] = false
}
});
},
zindex: 1000,
revert: false,
quiet: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
delay: 0
};
if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
Object.extend(defaults, {
starteffect: function(element) {
element._opacity = Element.getOpacity(element);
Draggable._dragging[element] = true;
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
}
});
var options = Object.extend(defaults, arguments[1] || { });
this.element = $(element);
if(options.handle && Object.isString(options.handle))
this.handle = this.element.down('.'+options.handle, 0);
if(!this.handle) this.handle = $(options.handle);
if(!this.handle) this.handle = this.element;
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
options.scroll = $(options.scroll);
this._isScrollChild = Element.childOf(this.element, options.scroll);
}
Element.makePositioned(this.element); // fix IE
this.options = options;
this.dragging = false;
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
Event.observe(this.handle, "mousedown", this.eventMouseDown);
Draggables.register(this);
},
destroy: function() {
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
Draggables.unregister(this);
},
currentDelta: function() {
return([
parseInt(Element.getStyle(this.element,'left') || '0'),
parseInt(Element.getStyle(this.element,'top') || '0')]);
},
initDrag: function(event) {
if(!Object.isUndefined(Draggable._dragging[this.element]) &&
Draggable._dragging[this.element]) return;
if(Event.isLeftClick(event)) {
// abort on form elements, fixes a Firefox issue
var src = Event.element(event);
if((tag_name = src.tagName.toUpperCase()) && (
tag_name=='INPUT' ||
tag_name=='SELECT' ||
tag_name=='OPTION' ||
tag_name=='BUTTON' ||
tag_name=='TEXTAREA')) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var pos = Position.cumulativeOffset(this.element);
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
Draggables.activate(this);
Event.stop(event);
}
},
startDrag: function(event) {
this.dragging = true;
if(!this.delta)
this.delta = this.currentDelta();
if(this.options.zindex) {
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
this.element.style.zIndex = this.options.zindex;
}
if(this.options.ghosting) {
this._clone = this.element.cloneNode(true);
this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
if (!this._originallyAbsolute)
Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element);
}
if(this.options.scroll) {
if (this.options.scroll == window) {
var where = this._getWindowScroll(this.options.scroll);
this.originalScrollLeft = where.left;
this.originalScrollTop = where.top;
} else {
this.originalScrollLeft = this.options.scroll.scrollLeft;
this.originalScrollTop = this.options.scroll.scrollTop;
}
}
Draggables.notify('onStart', this, event);
if(this.options.starteffect) this.options.starteffect(this.element);
},
updateDrag: function(event, pointer) {
if(!this.dragging) this.startDrag(event);
if(!this.options.quiet){
Position.prepare();
Droppables.show(pointer, this.element);
}
Draggables.notify('onDrag', this, event);
this.draw(pointer);
if(this.options.change) this.options.change(this);
if(this.options.scroll) {
this.stopScrolling();
var p;
if (this.options.scroll == window) {
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
} else {
p = Position.page(this.options.scroll);
p[0] += this.options.scroll.scrollLeft + Position.deltaX;
p[1] += this.options.scroll.scrollTop + Position.deltaY;
p.push(p[0]+this.options.scroll.offsetWidth);
p.push(p[1]+this.options.scroll.offsetHeight);
}
var speed = [0,0];
if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
this.startScrolling(speed);
}
// fix AppleWebKit rendering
if(Prototype.Browser.WebKit) window.scrollBy(0,0);
Event.stop(event);
},
finishDrag: function(event, success) {
this.dragging = false;
if(this.options.quiet){
Position.prepare();
var pointer = [Event.pointerX(event), Event.pointerY(event)];
Droppables.show(pointer, this.element);
}
if(this.options.ghosting) {
if (!this._originallyAbsolute)
Position.relativize(this.element);
delete this._originallyAbsolute;
Element.remove(this._clone);
this._clone = null;
}
var dropped = false;
if(success) {
dropped = Droppables.fire(event, this.element);
if (!dropped) dropped = false;
}
if(dropped && this.options.onDropped) this.options.onDropped(this.element);
Draggables.notify('onEnd', this, event);
var revert = this.options.revert;
if(revert && Object.isFunction(revert)) revert = revert(this.element);
var d = this.currentDelta();
if(revert && this.options.reverteffect) {
if (dropped == 0 || revert != 'failure')
this.options.reverteffect(this.element,
d[1]-this.delta[1], d[0]-this.delta[0]);
} else {
this.delta = d;
}
if(this.options.zindex)
this.element.style.zIndex = this.originalZ;
if(this.options.endeffect)
this.options.endeffect(this.element);
Draggables.deactivate(this);
Droppables.reset();
},
keyPress: function(event) {
if(event.keyCode!=Event.KEY_ESC) return;
this.finishDrag(event, false);
Event.stop(event);
},
endDrag: function(event) {
if(!this.dragging) return;
this.stopScrolling();
this.finishDrag(event, true);
Event.stop(event);
},
draw: function(point) {
var pos = Position.cumulativeOffset(this.element);
if(this.options.ghosting) {
var r = Position.realOffset(this.element);
pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
}
var d = this.currentDelta();
pos[0] -= d[0]; pos[1] -= d[1];
if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
}
var p = [0,1].map(function(i){
return (point[i]-pos[i]-this.offset[i])
}.bind(this));
if(this.options.snap) {
if(Object.isFunction(this.options.snap)) {
p = this.options.snap(p[0],p[1],this);
} else {
if(Object.isArray(this.options.snap)) {
p = p.map( function(v, i) {
return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
} else {
p = p.map( function(v) {
return (v/this.options.snap).round()*this.options.snap }.bind(this));
}
}}
var style = this.element.style;
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
style.left = p[0] + "px";
if((!this.options.constraint) || (this.options.constraint=='vertical'))
style.top = p[1] + "px";
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
},
stopScrolling: function() {
if(this.scrollInterval) {
clearInterval(this.scrollInterval);
this.scrollInterval = null;
Draggables._lastScrollPointer = null;
}
},
startScrolling: function(speed) {
if(!(speed[0] || speed[1])) return;
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
this.lastScrolled = new Date();
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
},
scroll: function() {
var current = new Date();
var delta = current - this.lastScrolled;
this.lastScrolled = current;
if(this.options.scroll == window) {
with (this._getWindowScroll(this.options.scroll)) {
if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
var d = delta / 1000;
this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
}
}
} else {
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
}
Position.prepare();
Droppables.show(Draggables._lastPointer, this.element);
Draggables.notify('onDrag', this);
if (this._isScrollChild) {
Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
if (Draggables._lastScrollPointer[0] < 0)
Draggables._lastScrollPointer[0] = 0;
if (Draggables._lastScrollPointer[1] < 0)
Draggables._lastScrollPointer[1] = 0;
this.draw(Draggables._lastScrollPointer);
}
if(this.options.change) this.options.change(this);
},
_getWindowScroll: function(w) {
var T, L, W, H;
with (w.document) {
if (w.document.documentElement && documentElement.scrollTop) {
T = documentElement.scrollTop;
L = documentElement.scrollLeft;
} else if (w.document.body) {
T = body.scrollTop;
L = body.scrollLeft;
}
if (w.innerWidth) {
W = w.innerWidth;
H = w.innerHeight;
} else if (w.document.documentElement && documentElement.clientWidth) {
W = documentElement.clientWidth;
H = documentElement.clientHeight;
} else {
W = body.offsetWidth;
H = body.offsetHeight;
}
}
return { top: T, left: L, width: W, height: H };
}
});
Draggable._dragging = { };
/*--------------------------------------------------------------------------*/
var SortableObserver = Class.create({
initialize: function(element, observer) {
this.element = $(element);
this.observer = observer;
this.lastValue = Sortable.serialize(this.element);
},
onStart: function() {
this.lastValue = Sortable.serialize(this.element);
},
onEnd: function() {
Sortable.unmark();
if(this.lastValue != Sortable.serialize(this.element))
this.observer(this.element)
}
});
var Sortable = {
SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
sortables: { },
_findRootElement: function(element) {
while (element.tagName.toUpperCase() != "BODY") {
if(element.id && Sortable.sortables[element.id]) return element;
element = element.parentNode;
}
},
options: function(element) {
element = Sortable._findRootElement($(element));
if(!element) return;
return Sortable.sortables[element.id];
},
destroy: function(element){
element = $(element);
var s = Sortable.sortables[element.id];
if(s) {
Draggables.removeObserver(s.element);
s.droppables.each(function(d){ Droppables.remove(d) });
s.draggables.invoke('destroy');
delete Sortable.sortables[s.element.id];
}
},
create: function(element) {
element = $(element);
var options = Object.extend({
element: element,
tag: 'li', // assumes li children, override with tag: 'tagname'
dropOnEmpty: false,
tree: false,
treeTag: 'ul',
overlap: 'vertical', // one of 'vertical', 'horizontal'
constraint: 'vertical', // one of 'vertical', 'horizontal', false
containment: element, // also takes array of elements (or id's); or false
handle: false, // or a CSS class
only: false,
delay: 0,
hoverclass: null,
ghosting: false,
quiet: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
format: this.SERIALIZE_RULE,
// these take arrays of elements or ids and can be
// used for better initialization performance
elements: false,
handles: false,
onChange: Prototype.emptyFunction,
onUpdate: Prototype.emptyFunction
}, arguments[1] || { });
// clear any old sortable with same element
this.destroy(element);
// build options for the draggables
var options_for_draggable = {
revert: true,
quiet: options.quiet,
scroll: options.scroll,
scrollSpeed: options.scrollSpeed,
scrollSensitivity: options.scrollSensitivity,
delay: options.delay,
ghosting: options.ghosting,
constraint: options.constraint,
handle: options.handle };
if(options.starteffect)
options_for_draggable.starteffect = options.starteffect;
if(options.reverteffect)
options_for_draggable.reverteffect = options.reverteffect;
else
if(options.ghosting) options_for_draggable.reverteffect = function(element) {
element.style.top = 0;
element.style.left = 0;
};
if(options.endeffect)
options_for_draggable.endeffect = options.endeffect;
if(options.zindex)
options_for_draggable.zindex = options.zindex;
// build options for the droppables
var options_for_droppable = {
overlap: options.overlap,
containment: options.containment,
tree: options.tree,
hoverclass: options.hoverclass,
onHover: Sortable.onHover
};
var options_for_tree = {
onHover: Sortable.onEmptyHover,
overlap: options.overlap,
containment: options.containment,
hoverclass: options.hoverclass
};
// fix for gecko engine
Element.cleanWhitespace(element);
options.draggables = [];
options.droppables = [];
// drop on empty handling
if(options.dropOnEmpty || options.tree) {
Droppables.add(element, options_for_tree);
options.droppables.push(element);
}
(options.elements || this.findElements(element, options) || []).each( function(e,i) {
var handle = options.handles ? $(options.handles[i]) :
(options.handle ? $(e).select('.' + options.handle)[0] : e);
options.draggables.push(
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
Droppables.add(e, options_for_droppable);
if(options.tree) e.treeNode = element;
options.droppables.push(e);
});
if(options.tree) {
(Sortable.findTreeElements(element, options) || []).each( function(e) {
Droppables.add(e, options_for_tree);
e.treeNode = element;
options.droppables.push(e);
});
}
// keep reference
this.sortables[element.id] = options;
// for onupdate
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
},
// return all suitable-for-sortable elements in a guaranteed order
findElements: function(element, options) {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.tag);
},
findTreeElements: function(element, options) {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.treeTag);
},
onHover: function(element, dropon, overlap) {
if(Element.isParent(dropon, element)) return;
if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
return;
} else if(overlap>0.5) {
Sortable.mark(dropon, 'before');
if(dropon.previousSibling != element) {
var oldParentNode = element.parentNode;
element.style.visibility = "hidden"; // fix gecko rendering
dropon.parentNode.insertBefore(element, dropon);
if(dropon.parentNode!=oldParentNode)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
} else {
Sortable.mark(dropon, 'after');
var nextElement = dropon.nextSibling || null;
if(nextElement != element) {
var oldParentNode = element.parentNode;
element.style.visibility = "hidden"; // fix gecko rendering
dropon.parentNode.insertBefore(element, nextElement);
if(dropon.parentNode!=oldParentNode)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
}
},
onEmptyHover: function(element, dropon, overlap) {
var oldParentNode = element.parentNode;
var droponOptions = Sortable.options(dropon);
if(!Element.isParent(dropon, element)) {
var index;
var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
var child = null;
if(children) {
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
for (index = 0; index < children.length; index += 1) {
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
offset -= Element.offsetSize (children[index], droponOptions.overlap);
} else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
child = index + 1 < children.length ? children[index + 1] : null;
break;
} else {
child = children[index];
break;
}
}
}
dropon.insertBefore(element, child);
Sortable.options(oldParentNode).onChange(element);
droponOptions.onChange(element);
}
},
unmark: function() {
if(Sortable._marker) Sortable._marker.hide();
},
mark: function(dropon, position) {
// mark on ghosting only
var sortable = Sortable.options(dropon.parentNode);
if(sortable && !sortable.ghosting) return;
if(!Sortable._marker) {
Sortable._marker =
($('dropmarker') || Element.extend(document.createElement('DIV'))).
hide().addClassName('dropmarker').setStyle({position:'absolute'});
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
}
var offsets = Position.cumulativeOffset(dropon);
Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
if(position=='after')
if(sortable.overlap == 'horizontal')
Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
else
Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
Sortable._marker.show();
},
_tree: function(element, options, parent) {
var children = Sortable.findElements(element, options) || [];
for (var i = 0; i < children.length; ++i) {
var match = children[i].id.match(options.format);
if (!match) continue;
var child = {
id: encodeURIComponent(match ? match[1] : null),
element: element,
parent: parent,
children: [],
position: parent.children.length,
container: $(children[i]).down(options.treeTag)
};
/* Get the element containing the children and recurse over it */
if (child.container)
this._tree(child.container, options, child);
parent.children.push (child);
}
return parent;
},
tree: function(element) {
element = $(element);
var sortableOptions = this.options(element);
var options = Object.extend({
tag: sortableOptions.tag,
treeTag: sortableOptions.treeTag,
only: sortableOptions.only,
name: element.id,
format: sortableOptions.format
}, arguments[1] || { });
var root = {
id: null,
parent: null,
children: [],
container: element,
position: 0
};
return Sortable._tree(element, options, root);
},
/* Construct a [i] index for a particular node */
_constructIndex: function(node) {
var index = '';
do {
if (node.id) index = '[' + node.position + ']' + index;
} while ((node = node.parent) != null);
return index;
},
sequence: function(element) {
element = $(element);
var options = Object.extend(this.options(element), arguments[1] || { });
return $(this.findElements(element, options) || []).map( function(item) {
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
});
},
setSequence: function(element, new_sequence) {
element = $(element);
var options = Object.extend(this.options(element), arguments[2] || { });
var nodeMap = { };
this.findElements(element, options).each( function(n) {
if (n.id.match(options.format))
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
n.parentNode.removeChild(n);
});
new_sequence.each(function(ident) {
var n = nodeMap[ident];
if (n) {
n[1].appendChild(n[0]);
delete nodeMap[ident];
}
});
},
serialize: function(element) {
element = $(element);
var options = Object.extend(Sortable.options(element), arguments[1] || { });
var name = encodeURIComponent(
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
if (options.tree) {
return Sortable.tree(element, arguments[1]).children.map( function (item) {
return [name + Sortable._constructIndex(item) + "[id]=" +
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
}).flatten().join('&');
} else {
return Sortable.sequence(element, arguments[1]).map( function(item) {
return name + "[]=" + encodeURIComponent(item);
}).join('&');
}
}
};
// Returns true if child is contained within element
Element.isParent = function(child, element) {
if (!child.parentNode || child == element) return false;
if (child.parentNode == element) return true;
return Element.isParent(child.parentNode, element);
};
Element.findChildren = function(element, only, recursive, tagName) {
if(!element.hasChildNodes()) return null;
tagName = tagName.toUpperCase();
if(only) only = [only].flatten();
var elements = [];
$A(element.childNodes).each( function(e) {
if(e.tagName && e.tagName.toUpperCase()==tagName &&
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
elements.push(e);
if(recursive) {
var grandchildren = Element.findChildren(e, only, recursive, tagName);
if(grandchildren) elements.push(grandchildren);
}
});
return (elements.length>0 ? elements.flatten() : []);
};
Element.offsetSize = function (element, type) {
return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,202 +0,0 @@
(function() {
Ajax.Responders.register({
onCreate: function(request) {
var token = $$('meta[name=csrf-token]')[0];
if (token) {
if (!request.options.requestHeaders) request.options.requestHeaders = {};
request.options.requestHeaders['X-CSRF-Token'] = token.readAttribute('content');
}
}
});
// Technique from Juriy Zaytsev
// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
function isEventSupported(eventName) {
var el = document.createElement('div');
eventName = 'on' + eventName;
var isSupported = (eventName in el);
if (!isSupported) {
el.setAttribute(eventName, 'return;');
isSupported = typeof el[eventName] == 'function';
}
el = null;
return isSupported;
}
function isForm(element) {
return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM';
}
function isInput(element) {
if (Object.isElement(element)) {
var name = element.nodeName.toUpperCase();
return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA';
}
else return false;
}
var submitBubbles = isEventSupported('submit'),
changeBubbles = isEventSupported('change');
if (!submitBubbles || !changeBubbles) {
// augment the Event.Handler class to observe custom events when needed
Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
function(init, element, eventName, selector, callback) {
init(element, eventName, selector, callback);
// is the handler being attached to an element that doesn't support this event?
if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
(!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
// "submit" => "emulated:submit"
this.eventName = 'emulated:' + this.eventName;
}
}
);
}
if (!submitBubbles) {
// discover forms on the page by observing focus events which always bubble
document.on('focusin', 'form', function(focusEvent, form) {
// special handler for the real "submit" event (one-time operation)
if (!form.retrieve('emulated:submit')) {
form.on('submit', function(submitEvent) {
var emulated = form.fire('emulated:submit', submitEvent, true);
// if custom event received preventDefault, cancel the real one too
if (emulated.returnValue === false) submitEvent.preventDefault();
});
form.store('emulated:submit', true);
}
});
}
if (!changeBubbles) {
// discover form inputs on the page
document.on('focusin', 'input, select, textarea', function(focusEvent, input) {
// special handler for real "change" events
if (!input.retrieve('emulated:change')) {
input.on('change', function(changeEvent) {
input.fire('emulated:change', changeEvent, true);
});
input.store('emulated:change', true);
}
});
}
function handleRemote(element) {
var method, url, params;
var event = element.fire("ajax:before");
if (event.stopped) return false;
if (element.tagName.toLowerCase() === 'form') {
method = element.readAttribute('method') || 'post';
url = element.readAttribute('action');
// serialize the form with respect to the submit button that was pressed
params = element.serialize({ submit: element.retrieve('rails:submit-button') });
// clear the pressed submit button information
element.store('rails:submit-button', null);
} else {
method = element.readAttribute('data-method') || 'get';
url = element.readAttribute('href');
params = {};
}
new Ajax.Request(url, {
method: method,
parameters: params,
evalScripts: true,
onCreate: function(response) { element.fire("ajax:create", response); },
onComplete: function(response) { element.fire("ajax:complete", response); },
onSuccess: function(response) { element.fire("ajax:success", response); },
onFailure: function(response) { element.fire("ajax:failure", response); }
});
element.fire("ajax:after");
}
function insertHiddenField(form, name, value) {
form.insert(new Element('input', { type: 'hidden', name: name, value: value }));
}
function handleMethod(element) {
var method = element.readAttribute('data-method'),
url = element.readAttribute('href'),
csrf_param = $$('meta[name=csrf-param]')[0],
csrf_token = $$('meta[name=csrf-token]')[0];
var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
$(element.parentNode).insert(form);
if (method !== 'post') {
insertHiddenField(form, '_method', method);
}
if (csrf_param) {
insertHiddenField(form, csrf_param.readAttribute('content'), csrf_token.readAttribute('content'));
}
form.submit();
}
function disableFormElements(form) {
form.select('input[type=submit][data-disable-with]').each(function(input) {
input.store('rails:original-value', input.getValue());
input.setValue(input.readAttribute('data-disable-with')).disable();
});
}
function enableFormElements(form) {
form.select('input[type=submit][data-disable-with]').each(function(input) {
input.setValue(input.retrieve('rails:original-value')).enable();
});
}
function allowAction(element) {
var message = element.readAttribute('data-confirm');
return !message || confirm(message);
}
document.on('click', 'a[data-confirm], a[data-remote], a[data-method]', function(event, link) {
if (!allowAction(link)) {
event.stop();
return false;
}
if (link.readAttribute('data-remote')) {
handleRemote(link);
event.stop();
} else if (link.readAttribute('data-method')) {
handleMethod(link);
event.stop();
}
});
document.on("click", "form input[type=submit], form button[type=submit], form button:not([type])", function(event, button) {
// register the pressed submit button
event.findElement('form').store('rails:submit-button', button.name || false);
});
document.on("submit", function(event) {
var form = event.findElement();
if (!allowAction(form)) {
event.stop();
return false;
}
if (form.readAttribute('data-remote')) {
handleRemote(form);
event.stop();
} else {
disableFormElements(form);
}
});
document.on('ajax:create', 'form', function(event, form) {
if (form == event.findElement()) disableFormElements(form);
});
document.on('ajax:complete', 'form', function(event, form) {
if (form == event.findElement()) enableFormElements(form);
});
})();

Some files were not shown because too many files have changed in this diff Show More