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:
parent
387836f8aa
commit
6a2ca5e034
2
Gemfile
2
Gemfile
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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
|
||||
%>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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| -%>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element.update('tab-content-memberships', '<%= escape_javascript(render :partial => 'groups/memberships') %>');
|
||||
$('#tab-content-memberships').html('<%= escape_javascript(render :partial => 'groups/memberships') %>');
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element.update('tab-content-users', '<%= escape_javascript(render :partial => 'groups/users') %>');
|
||||
$('#tab-content-users').html('<%= escape_javascript(render :partial => 'groups/users') %>');
|
||||
|
|
|
@ -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) %>');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();" %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element.remove('<%= "relation-#{@relation.id}" %>');
|
||||
$('#relation-<%= @relation.id %>').remove();
|
||||
|
|
|
@ -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' %>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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') %>
|
||||
|
|
|
@ -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}%>
|
||||
|
|
|
@ -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');
|
||||
<% else %>
|
||||
Element.hide('log_time');
|
||||
<% end %>
|
||||
}
|
||||
<% if User.current.allowed_to?(:log_time, @issue.project) %>
|
||||
$('#log_time').show();
|
||||
<% else %>
|
||||
$('#log_time').hide();
|
||||
<% end %>
|
||||
|
|
|
@ -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') %>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
$('#content').html('<%= escape_javascript(render :template => 'issues/bulk_edit.html') %>');
|
|
@ -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) %>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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') %>');
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 }) %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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(', ') %>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) -%>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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();});' %>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}')"
|
||||
) %>"> </span>
|
||||
:parent_id => tr_id)) %>');"> </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},
|
||||
|
|
|
@ -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) %>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
$('#content').html('<%= escape_javascript(render :template => 'repositories/new.html') %>');
|
|
@ -1 +1 @@
|
|||
Element.remove('related-issue-<%= @issue.id %>');
|
||||
$('#related-issue-<%= @issue.id %>').remove();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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]] +
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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') %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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? %>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element.update('tab-content-memberships', '<%= escape_javascript(render :partial => 'users/memberships') %>');
|
||||
$('#tab-content-memberships').html('<%= escape_javascript(render :partial => 'users/memberships') %>');
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) %>');
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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])) %>');
|
||||
|
|
|
@ -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)) %>
|
||||
|
|
|
@ -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) %>")});
|
||||
|
|
|
@ -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)) %>');
|
||||
|
|
|
@ -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})) %>');
|
||||
|
|
|
@ -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})) %>');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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') %>');
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
require 'prototype_legacy_helper'
|
|
@ -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
|
|
@ -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&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&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
|
|
@ -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)
|
||||
|
|
|
@ -1,70 +1,65 @@
|
|||
/* Redmine - project management software
|
||||
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;
|
||||
}
|
||||
function checkAll(id, 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');})}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
$("#list-top, #list-left, #list-right").disableSelection();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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];
|
||||
var warnLeavingUnsavedMessage;
|
||||
function warnLeavingUnsaved(message) {
|
||||
warnLeavingUnsavedMessage = message;
|
||||
|
||||
if (csrf_meta_tag) {
|
||||
var header = 'X-CSRF-Token',
|
||||
token = csrf_meta_tag.readAttribute('content');
|
||||
$('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;}
|
||||
};
|
||||
};
|
||||
|
||||
if (!request.options.requestHeaders) {
|
||||
request.options.requestHeaders = {};
|
||||
}
|
||||
request.options.requestHeaders[header] = token;
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
$(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);
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
function contextMenuAddSelection(tr) {
|
||||
tr.addClass('context-menu-selection');
|
||||
contextMenuCheckSelectionBox(tr, true);
|
||||
contextMenuClearDocumentSelection();
|
||||
}
|
||||
|
||||
unselectAll: function() {
|
||||
var rows = $$('.hascontextmenu');
|
||||
for (i=0; i<rows.length; i++) {
|
||||
this.removeSelection(rows[i]);
|
||||
}
|
||||
},
|
||||
function contextMenuRemoveSelection(tr) {
|
||||
tr.removeClass('context-menu-selection');
|
||||
contextMenuCheckSelectionBox(tr, false);
|
||||
}
|
||||
|
||||
checkSelectionBox: function(tr, checked) {
|
||||
var inputs = Element.getElementsBySelector(tr, 'input');
|
||||
if (inputs.length > 0) { inputs[0].checked = checked; }
|
||||
},
|
||||
function contextMenuIsSelected(tr) {
|
||||
return tr.hasClass('context-menu-selection');
|
||||
}
|
||||
|
||||
isSelected: function(tr) {
|
||||
return Element.hasClassName(tr, 'context-menu-selection');
|
||||
},
|
||||
function contextMenuCheckSelectionBox(tr, checked) {
|
||||
tr.find('input[type=checkbox]').attr('checked', checked);
|
||||
}
|
||||
|
||||
clearDocumentSelection: function() {
|
||||
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() {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
});
|
|
@ -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
|
@ -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
Loading…
Reference in New Issue