mirror of
https://github.com/TracksApp/tracks.git
synced 2026-01-25 10:16:11 +01:00
Merge branch 'master' of git://github.com/bsag/tracks into bsag
Conflicts: app/views/todos/_todo.html.erb
This commit is contained in:
commit
deacdbda29
17 changed files with 138 additions and 35 deletions
|
|
@ -123,6 +123,12 @@ class ApplicationController < ActionController::Base
|
|||
formatted_date
|
||||
end
|
||||
|
||||
|
||||
def for_autocomplete(coll, substr)
|
||||
filtered = coll.find_all{|item| item.name.downcase.include? substr.downcase}
|
||||
return filtered.map {|item| "#{item.name}|#{item.id}"}.join("\n")
|
||||
end
|
||||
|
||||
# Uses RedCloth to transform text using either Textile or Markdown Need to
|
||||
# require redcloth above RedCloth 3.0 or greater is needed to use Markdown,
|
||||
# otherwise it only handles Textile
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class ContextsController < ApplicationController
|
|||
format.rss &render_contexts_rss_feed
|
||||
format.atom &render_contexts_atom_feed
|
||||
format.text { render :action => 'index', :layout => false, :content_type => Mime::TEXT }
|
||||
format.autocomplete { render :text => for_autocomplete(@active_contexts + @hidden_contexts, params[:q])}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class ProjectsController < ApplicationController
|
|||
format.rss &render_rss_feed
|
||||
format.atom &render_atom_feed
|
||||
format.text &render_text_feed
|
||||
format.autocomplete { render :text => for_autocomplete(@projects, params[:q]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -526,6 +526,13 @@ class TodosController < ApplicationController
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
def tags
|
||||
@tags = Tag.all
|
||||
respond_to do |format|
|
||||
format.autocomplete { render :text => for_autocomplete(@tags, params[:q]) }
|
||||
end
|
||||
end
|
||||
|
||||
def defer
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
|
|
|
|||
|
|
@ -202,9 +202,11 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def format_note(note)
|
||||
note.gsub!(/</, '<') # eliminate tags
|
||||
note.gsub!(/>/, '>')
|
||||
note = markdown(note)
|
||||
note = auto_link_message(note)
|
||||
note = auto_link(note)
|
||||
note = markdown(note)
|
||||
note = sanitize(note)
|
||||
return note
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,4 +2,8 @@ module NotesHelper
|
|||
def truncated_note(note, characters = 50)
|
||||
sanitize(textilize_without_paragraph(truncate(note.body, :length => characters, :omission => "...")))
|
||||
end
|
||||
|
||||
def rendered_note(note)
|
||||
sanitize(textilize_without_paragraph(note.body))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -276,20 +276,6 @@ module TodosHelper
|
|||
return "c#{todo.context_id}empty-nd"
|
||||
end
|
||||
|
||||
def project_names_for_autocomplete
|
||||
array_or_string_for_javascript( ['None'] + current_user.projects.active.collect{|p| escape_javascript(p.name) } )
|
||||
end
|
||||
|
||||
def context_names_for_autocomplete
|
||||
# #return array_or_string_for_javascript(['Create a new context']) if
|
||||
# @contexts.empty?
|
||||
array_or_string_for_javascript( current_user.contexts.collect{|c| escape_javascript(c.name) } )
|
||||
end
|
||||
|
||||
def tag_names_for_autocomplete
|
||||
array_or_string_for_javascript( Tag.all.collect{|c| escape_javascript(c.name) } )
|
||||
end
|
||||
|
||||
def default_contexts_for_autocomplete
|
||||
projects = current_user.projects.find(:all, :conditions => ['default_context_id is not null'])
|
||||
Hash[*projects.map{ |p| [p.name, p.default_context.name] }.flatten].to_json
|
||||
|
|
|
|||
|
|
@ -6,18 +6,16 @@
|
|||
'jquery.autocomplete', :cache => true %>
|
||||
<%= stylesheet_link_tag "print", :media => "print" %>
|
||||
<%= javascript_include_tag 'jquery','jquery-ui','jquery.cookie',
|
||||
'jquery.blockUI','jquery.jeditable','jquery.autocomplete', :cache => 'jquery-all' %>
|
||||
'jquery.blockUI','jquery.jeditable','jquery.autocomplete',
|
||||
'jquery.truncator', :cache => 'jquery-all' %>
|
||||
<%= javascript_include_tag 'hoverIntent','superfish','application',
|
||||
'accesskey-hints','niftycube','flashobject', :cache => 'tracks' %>
|
||||
<%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %>
|
||||
<%= javascript_tag "var SOURCE_VIEW = '#{@source_view}';" %>
|
||||
<%= javascript_tag "var TAG_NAME = '#{@tag_name}';" if @tag_name %>
|
||||
<script type="text/javascript">
|
||||
var contextNames = <%= context_names_for_autocomplete rescue '[]' %>;
|
||||
var projectNames = <%= project_names_for_autocomplete rescue '[]' %>;
|
||||
var defaultContexts = <%= default_contexts_for_autocomplete rescue '{}' %>;
|
||||
var defaultTags = <%= default_tags_for_autocomplete rescue '{}' %>;
|
||||
var tagNames = <%= tag_names_for_autocomplete rescue '[]' %>;
|
||||
var dateFormat = '<%= date_format_for_date_picker %>';
|
||||
var weekStart = '<%= current_user.prefs.week_starts %>';
|
||||
function relative_to_root(path) { return '<%= root_url %>'+path; };
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<% note = notes_summary -%>
|
||||
<div class="note_wrapper" id="<%= dom_id(note) %>">
|
||||
<%= link_to( image_tag("blank.png", :border => 0), note_path(note), :title => "Show note", :class => "link_to_notes icon") %>
|
||||
<%= truncated_note(note) %>
|
||||
<%= rendered_note(note) %>
|
||||
</div>
|
||||
<% note = nil -%>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
|
|||
<div class="description<%= staleness_class( todo ) %>">
|
||||
<%= grip_span %>
|
||||
<%= date_span -%>
|
||||
<span class="todo.descr"><%= sanitize(todo.description) %></span>
|
||||
<span class="todo.descr"><%= h todo.description %></span>
|
||||
<% #= successors_span %>
|
||||
<%= image_tag_for_recurring_todo(todo) if @todo.from_recurring_todo? %>
|
||||
<%= tag_list %>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# Add new mime types for use in respond_to blocks:
|
||||
# Mime::Type.register "text/richtext", :rtf
|
||||
# Mime::Type.register "application/x-mobile", :mobile
|
||||
Mime::Type.register_alias "text/html", :m
|
||||
Mime::Type.register_alias "text/html", :m
|
||||
Mime::Type.register_alias "text/plain", :autocomplete
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ ActionController::Routing::Routes.draw do |map|
|
|||
# routed to mobile view of tags.
|
||||
todos.tag 'todos/tag/:name.m', :action => "tag", :format => 'm'
|
||||
todos.tag 'todos/tag/:name', :action => "tag", :name => /.*/
|
||||
|
||||
todos.tags 'tags.autocomplete', :action => "tags", :format => 'autocomplete'
|
||||
todos.auto_complete_for_predecessor 'auto_complete_for_predecessor', :action => 'auto_complete_for_predecessor'
|
||||
|
||||
todos.calendar 'calendar.ics', :action => "calendar", :format => 'ics'
|
||||
todos.calendar 'calendar', :action => "calendar"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -170,10 +170,16 @@ function update_order(event, ui){
|
|||
/* Unobtrusive jQuery behavior */
|
||||
|
||||
function project_defaults(){
|
||||
if(defaultContexts[$(this).val()] !== undefined) {
|
||||
context_name = $(this).parents('form').find('input[name=context_name]');
|
||||
if(context_name.attr('edited') === undefined){
|
||||
context_name.val(defaultContexts[$(this).val()]);
|
||||
if($('body').hasClass('contexts')){
|
||||
// don't change the context
|
||||
// see ticket #934
|
||||
}
|
||||
else {
|
||||
if(defaultContexts[$(this).val()] !== undefined) {
|
||||
context_name = $(this).parents('form').find('input[name=context_name]');
|
||||
if(context_name.attr('edited') === undefined){
|
||||
context_name.val(defaultContexts[$(this).val()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(defaultTags[$(this).val()] !== undefined) {
|
||||
|
|
@ -187,11 +193,16 @@ function project_defaults(){
|
|||
function enable_rich_interaction(){
|
||||
$('input.Date').datepicker({'dateFormat': dateFormat, 'firstDay': weekStart});
|
||||
/* Autocomplete */
|
||||
$('input[name=context_name]').autocomplete(contextNames, {matchContains: true});
|
||||
$('input[name=project[default_context_name]]').autocomplete(contextNames, {matchContains: true});
|
||||
$('input[name=project_name]').autocomplete(projectNames, {matchContains: true});
|
||||
$('input[name=tag_list]:not(.ac_input)').autocomplete(tagNames, {multiple: true,multipleSeparator:',',matchContains:true});
|
||||
$('input[name=predecessor_list]:not(.ac_input)').autocomplete('/todos/auto_complete_for_predecessor',
|
||||
$('input[name=context_name]').autocomplete(
|
||||
relative_to_root('contexts.autocomplete'), {matchContains: true});
|
||||
$('input[name=project[default_context_name]]').autocomplete(
|
||||
relative_to_root('contexts.autocomplete'), {matchContains: true});
|
||||
$('input[name=project_name]').autocomplete(
|
||||
relative_to_root('projects.autocomplete'), {matchContains: true});
|
||||
$('input[name=tag_list]:not(.ac_input)').autocomplete(
|
||||
relative_to_root('tags.autocomplete'), {multiple: true,multipleSeparator:',',matchContains:true});
|
||||
$('input[name=predecessor_list]:not(.ac_input)').autocomplete(
|
||||
relative_to_root('auto_complete_for_predecessor'),
|
||||
{multiple: true,multipleSeparator:','});
|
||||
|
||||
/* have to bind on keypress because of limitataions of live() */
|
||||
|
|
@ -289,6 +300,8 @@ $(document).ready(function() {
|
|||
$(this).next().toggle("fast"); return false;
|
||||
});
|
||||
|
||||
$('.note_wrapper').truncate({max_length: 90, more: '', less: ''});
|
||||
|
||||
$(".show_successors").live('click', function () {
|
||||
$(this).next().toggle("fast"); return false;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
var cfg = {
|
||||
sensitivity: 7,
|
||||
interval: 100,
|
||||
timeout: 0
|
||||
timeout: 400
|
||||
};
|
||||
// override configuration options with user supplied object
|
||||
cfg = $.extend(cfg, g ? { over: f, out: g } : f );
|
||||
|
|
@ -81,4 +81,4 @@
|
|||
return this.mouseover(handleHover).mouseout(handleHover);
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
})(jQuery);
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ $.Autocompleter = function(input, options) {
|
|||
// limit abortion to this input
|
||||
port: "autocomplete" + input.name,
|
||||
dataType: options.dataType,
|
||||
type: 'POST',
|
||||
type: 'GET',
|
||||
url: options.url,
|
||||
data: $.extend({
|
||||
q: lastWord(term),
|
||||
|
|
|
|||
81
public/javascripts/jquery.truncator.js
Normal file
81
public/javascripts/jquery.truncator.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// HTML Truncator for jQuery
|
||||
// by Henrik Nyh <http://henrik.nyh.se> 2008-02-28.
|
||||
// modified by Eric Allen <http://hackerengineer.net> 2010-04-02
|
||||
// Free to modify and redistribute with credit.
|
||||
|
||||
(function($) {
|
||||
|
||||
var trailing_whitespace = true;
|
||||
|
||||
$.fn.truncate = function(options) {
|
||||
|
||||
var opts = $.extend({}, $.fn.truncate.defaults, options);
|
||||
|
||||
$(this).each(function() {
|
||||
|
||||
var content_length = $.trim(squeeze($(this).text())).length;
|
||||
if (content_length <= opts.max_length)
|
||||
return; // bail early if not overlong
|
||||
|
||||
var actual_max_length = opts.max_length - opts.more.length - 3; // 3 for " ()"
|
||||
var truncated_node = recursivelyTruncate(this, actual_max_length);
|
||||
var full_node = $(this).hide();
|
||||
|
||||
truncated_node.insertAfter(full_node);
|
||||
|
||||
findNodeForMore(truncated_node).append('...');
|
||||
});
|
||||
}
|
||||
|
||||
// Note that the " (…more)" bit counts towards the max length – so a max
|
||||
// length of 10 would truncate "1234567890" to "12 (…more)".
|
||||
$.fn.truncate.defaults = {
|
||||
max_length: 100,
|
||||
more: '…more',
|
||||
less: 'less'
|
||||
};
|
||||
|
||||
function recursivelyTruncate(node, max_length) {
|
||||
return (node.nodeType == 3) ? truncateText(node, max_length) : truncateNode(node, max_length);
|
||||
}
|
||||
|
||||
function truncateNode(node, max_length) {
|
||||
var node = $(node);
|
||||
var new_node = node.clone().empty();
|
||||
var truncatedChild;
|
||||
node.contents().each(function() {
|
||||
var remaining_length = max_length - new_node.text().length;
|
||||
if (remaining_length == 0) return; // breaks the loop
|
||||
truncatedChild = recursivelyTruncate(this, remaining_length);
|
||||
if (truncatedChild) new_node.append(truncatedChild);
|
||||
});
|
||||
return new_node;
|
||||
}
|
||||
|
||||
function truncateText(node, max_length) {
|
||||
var text = squeeze(node.data);
|
||||
if (trailing_whitespace) // remove initial whitespace if last text
|
||||
text = text.replace(/^ /, ''); // node had trailing whitespace.
|
||||
trailing_whitespace = !!text.match(/ $/);
|
||||
var text = text.slice(0, max_length);
|
||||
// Ensure HTML entities are encoded
|
||||
// http://debuggable.com/posts/encode-html-entities-with-jquery:480f4dd6-13cc-4ce9-8071-4710cbdd56cb
|
||||
text = $('<div/>').text(text).html();
|
||||
return text;
|
||||
}
|
||||
|
||||
// Collapses a sequence of whitespace into a single space.
|
||||
function squeeze(string) {
|
||||
return string.replace(/\s+/g, ' ');
|
||||
}
|
||||
|
||||
// Finds the last, innermost block-level element
|
||||
function findNodeForMore(node) {
|
||||
var $node = $(node);
|
||||
var last_child = $node.children(":last");
|
||||
if (!last_child) return node;
|
||||
var display = last_child.css('display');
|
||||
if (!display || display=='inline') return $node;
|
||||
return findNodeForMore(last_child);
|
||||
};
|
||||
})(jQuery);
|
||||
Loading…
Add table
Add a link
Reference in a new issue