diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 3163bf06..242fb1ef 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1,6 +1,6 @@ /* *= require bootstrap_and_overrides *= require foodsoft -*= require token-input +*= require token-input-bootstrappy *= require jquery.fancybox-1.3.4 */ \ No newline at end of file diff --git a/app/assets/stylesheets/nav.css b/app/assets/stylesheets/nav.css deleted file mode 100644 index 14ab6bc9..00000000 --- a/app/assets/stylesheets/nav.css +++ /dev/null @@ -1,58 +0,0 @@ -/* Navigation .... */ -#nav { - position: relative; - height: 3em; - text-align: right; - margin-top: 5px; } - #nav ul { - padding: 0 0 0 0; - margin: -2.3em 0 0 0; - list-style: none; } - #nav ul li { - display: inline; - list-style: none; - padding: 0.3em 0 0.3em 0; - margin: 0 0 0 0.2em; - background: none; - vertical-align: middle; } - #nav ul li a { - margin: 0.1em 0 0 0; - text-align: center; - font-size: 1.1em; - font-weight: bold; - text-decoration: none; - color: #737272; - border: none; - background: #e3e3e3; - padding: 0.5em 1.2em 0.6em 1.2em; - margin-top: 0; } - #nav ul li a:hover { - color: #ed0606; } - #nav ul li ul { - display: none; - position: absolute; - margin: 0; - left: 0; - padding: 0.6em; - bottom: -1.4em; - right: 0em; - background: #ed0606; } - #nav ul li ul li { - border-left: 0.1em dotted white; - background: none; } - #nav ul li ul a, #nav ul li ul a:hover { - border: none; - background: none; - color: white; - font-style: normal; - font-size: 1em; } - #nav ul li.current a { - border: none; - border-bottom: none; - background: #ed0606; - padding-bottom: 0.6em; - color: white; } - #nav ul li.current ul { - display: inline; } - #nav ul li.current ul a { - background: none; } diff --git a/app/assets/stylesheets/rails_messages.scss b/app/assets/stylesheets/rails_messages.scss deleted file mode 100644 index 376d19ed..00000000 --- a/app/assets/stylesheets/rails_messages.scss +++ /dev/null @@ -1,68 +0,0 @@ -/* rails messages .... */ -h3.notice, div.notice { - color: #090; } - -h3.notice { - background: #dfd image-url('icon_message.gif') no-repeat 10px 3px; - border: 1px solid #bfb; - padding: 4px 40px; - margin: 2em 0 1em 0; - font-size: 100%; - position: relative; } - -h3.alert, div.alert, div.error { - color: red; - font-size: 100%; - font-weight: bold; } - -h3.alert { - background: yellow image-url('error.png') no-repeat 10px 3px; - border: 1px solid red; - padding: 4px 40px; - position: relative; - margin: 2em 0 1em 0; - font-weight: normal; } - -.fieldWithErrors { - padding: 2px; - background-color: red; - display: table; } - -.fieldWithErrors input { - background-color: yellow; } - -#errorExplanation { - width: 40em; - padding: 7px; - padding-bottom: 12px; - color: #ed0606; } - #errorExplanation h2 { - color: #ed0606; - text-align: left; - font-weight: bold; - margin: 0; - padding: 5px 5px 5px 5px; - font-size: 15px; } - #errorExplanation p { - margin: 0; - padding: 5px; } - #errorExplanation ul, #errorExplanation li { - margin: 0; } - #errorExplanation li { - padding: 0; - font-size: 12px; - list-style: square; } - -div.uploadStatus { - margin: 5px; } - -div.progressBar { - margin: 5px; } - div.progressBar div.border { - background-color: #fff; - border: 1px solid grey; - width: 100%; } - div.progressBar div.background { - background-color: #333; - height: 18px; - width: 0%; } diff --git a/app/assets/stylesheets/scaffold.css b/app/assets/stylesheets/scaffold.css deleted file mode 100644 index 093c2099..00000000 --- a/app/assets/stylesheets/scaffold.css +++ /dev/null @@ -1,54 +0,0 @@ -body { background-color: #fff; color: #333; } - -body, p, ol, ul, td { - font-family: verdana, arial, helvetica, sans-serif; - font-size: 13px; - line-height: 18px; -} - -pre { - background-color: #eee; - padding: 10px; - font-size: 11px; -} - -a { color: #000; } -a:visited { color: #666; } -a:hover { color: #fff; background-color:#000; } - -.fieldWithErrors { - padding: 2px; - background-color: red; - display: table; -} - -#errorExplanation { - width: 400px; - border: 2px solid red; - padding: 7px; - padding-bottom: 12px; - margin-bottom: 20px; - background-color: #f0f0f0; -} - -#errorExplanation h2 { - text-align: left; - font-weight: bold; - padding: 5px 5px 5px 15px; - font-size: 12px; - margin: -7px; - background-color: #c00; - color: #fff; -} - -#errorExplanation p { - color: #333; - margin-bottom: 0; - padding: 5px; -} - -#errorExplanation ul li { - font-size: 12px; - list-style: square; -} - diff --git a/app/assets/stylesheets/simple_form.css b/app/assets/stylesheets/simple_form.css deleted file mode 100644 index cbd998ce..00000000 --- a/app/assets/stylesheets/simple_form.css +++ /dev/null @@ -1,42 +0,0 @@ -.simple_form label { - float: left; - width: 120px; - text-align: right; - margin: 2px 10px; -} - -.simple_form div.input { - margin-bottom: 10px; - clear: both; -} - -.simple_form div.boolean, .simple_form input[type='submit'] { - margin-left: 140px; -} - -.simple_form div.boolean label, .simple_form label.collection_radio { - float: none; - margin: 0; -} - -.simple_form label.collection_radio { - margin-right: 10px; - margin-left: 2px; -} - -.simple_form .error { - clear: left; - margin-left: 140px; - font-size: 12px; - color: #D00; - display: block; -} - -.simple_form .hint { - clear: left; - margin-left: 140px; - font-size: 12px; - color: #555; - display: block; - font-style: italic; -} \ No newline at end of file diff --git a/app/assets/stylesheets/token-input-bootstrappy.css b/app/assets/stylesheets/token-input-bootstrappy.css new file mode 100644 index 00000000..859414e1 --- /dev/null +++ b/app/assets/stylesheets/token-input-bootstrappy.css @@ -0,0 +1,133 @@ +/* Example tokeninput style #2: Facebook style */ +ul.token-input-list-facebook { + overflow: hidden; + height: auto !important; + height: 1%; + width: 400px; + border: 1px solid #BBB; + cursor: text; + font-size: 12px; + font-family: Verdana, sans-serif; + min-height: 1px; + z-index: 999; + margin: 0; + padding: 0; + background-color: #fff; + list-style-type: none; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +ul.token-input-list-facebook.token-input-focused-facebook { + border-color: rgba(82, 168, 236, 0.8); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + outline: 0; + outline: thin dotted 9; +} + +ul.token-input-list-facebook li input { + border: 0; + width: 100px; + padding: 3px 8px; + background-color: white; + margin: 2px 0; + -webkit-appearance: caret; +} + +li.token-input-token-facebook { + overflow: hidden; + height: auto !important; + height: 15px; + margin: 3px; + padding: 1px 3px; + background-color: #F5F5F5; + color: #555; + cursor: default; + border: 1px solid #BBB; + font-size: 11px; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + float: left; + white-space: nowrap; +} + +li.token-input-token-facebook p { + display: inline; + padding: 0; + margin: 0; +} + +li.token-input-token-facebook span { + color: #a6b3cf; + margin-left: 5px; + font-weight: bold; + cursor: pointer; +} + +li.token-input-selected-token-facebook { + background-color: #5670a6; + border: 1px solid #3b5998; + color: #fff; +} + +li.token-input-input-token-facebook { + float: left; + margin: 0; + padding: 0; + list-style-type: none; +} + +div.token-input-dropdown-facebook { + position: absolute; + width: 400px; + background-color: #F5F5F5; + overflow: hidden; + border-left: 1px solid #ccc; + border-right: 1px solid #ccc; + border-bottom: 1px solid #ccc; + cursor: default; + font-size: 12px; + font-family: Verdana, sans-serif; + z-index: 1; +} + +div.token-input-dropdown-facebook p { + margin: 0; + padding: 5px; + font-weight: bold; + color: #555; +} + +div.token-input-dropdown-facebook ul { + margin: 0; + padding: 0; +} + +div.token-input-dropdown-facebook ul li { + background-color: #f5f5f5; + padding: 3px; + margin: 0; + list-style-type: none; +} + +div.token-input-dropdown-facebook ul li.token-input-dropdown-item-facebook { + background-color: #f5f5f5; +} + +div.token-input-dropdown-facebook ul li.token-input-dropdown-item2-facebook { + background-color: #f5f5f5; +} + +div.token-input-dropdown-facebook ul li em { + font-weight: bold; + font-style: normal; +} + +div.token-input-dropdown-facebook ul li.token-input-selected-dropdown-item-facebook { + background-color: #08C; + color: #f5f5f5; +} \ No newline at end of file diff --git a/app/assets/stylesheets/token-input.css b/app/assets/stylesheets/token-input.css deleted file mode 100644 index 9a2a0811..00000000 --- a/app/assets/stylesheets/token-input.css +++ /dev/null @@ -1,112 +0,0 @@ -/* Example tokeninput style #1: Token vertical list*/ -ul.token-input-list { - overflow: hidden; - height: auto !important; - height: 1%; - width: 300px; - border: 1px solid #999; - cursor: text; - font-size: 12px; - font-family: Verdana; - z-index: 999; - margin: 0; - padding: 0; - background-color: #fff; - list-style-type: none; -} - -ul.token-input-list li { - list-style-type: none; -} - -ul.token-input-list li input { - border: 0; - width: 350px; - padding: 3px 8px; - background-color: white; - -webkit-appearance: caret; -} - -li.token-input-token { - overflow: hidden; - height: auto !important; - height: 1%; - margin: 3px; - padding: 3px 5px; - background-color: #d0efa0; - color: #000; - font-weight: bold; - cursor: default; - display: block; -} - -li.token-input-token p { - float: left; - padding: 0; - margin: 0; -} - -li.token-input-token span { - float: right; - color: #777; - cursor: pointer; -} - -li.token-input-selected-token { - background-color: #08844e; - color: #fff; -} - -li.token-input-selected-token span { - color: #bbb; -} - -div.token-input-dropdown { - position: absolute; - width: 400px; - background-color: #fff; - overflow: hidden; - border-left: 1px solid #ccc; - border-right: 1px solid #ccc; - border-bottom: 1px solid #ccc; - cursor: default; - font-size: 12px; - font-family: Verdana; - z-index: 1; -} - -div.token-input-dropdown p { - margin: 0; - padding: 5px; - font-weight: bold; - color: #777; -} - -div.token-input-dropdown ul { - margin: 0; - padding: 0; -} - -div.token-input-dropdown ul li { - background-color: #fff; - padding: 3px; - list-style-type: none; -} - -div.token-input-dropdown ul li.token-input-dropdown-item { - background-color: #fafafa; -} - -div.token-input-dropdown ul li.token-input-dropdown-item2 { - background-color: #fff; -} - -div.token-input-dropdown ul li em { - font-weight: bold; - font-style: normal; -} - -div.token-input-dropdown ul li.token-input-selected-dropdown-item { - background-color: #d0efa0; -} - diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index dbdfc90d..88224a98 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -2,7 +2,7 @@ class MessagesController < ApplicationController # Renders the "inbox" action. def index - @messages = Message.public.paginate :page => params[:page], :per_page => 20, :order => 'created_at DESC' + @messages = Message.public.page(params[:page]).per(@per_page).order('created_at DESC') end # Creates a new message object. diff --git a/app/views/messages/_messages.html.haml b/app/views/messages/_messages.html.haml index a43f2e2a..7db06dc4 100644 --- a/app/views/messages/_messages.html.haml +++ b/app/views/messages/_messages.html.haml @@ -1,3 +1,7 @@ +- if Message.public.count > 20 + = items_per_page += pagination_links_remote messages + - unless messages.empty? %table.table.table-striped %tbody diff --git a/app/views/messages/index.html.haml b/app/views/messages/index.html.haml index 74e90918..af064bc5 100644 --- a/app/views/messages/index.html.haml +++ b/app/views/messages/index.html.haml @@ -1,8 +1,5 @@ -- title "Nachrichten im Überblick" - -%p= link_to('Neue Nachricht', :action => 'new') - -%div{:style => "text-align:right"}= will_paginate @messages +- title "Nachrichten" += link_to 'Neue Nachricht', new_message_path, class: 'btn btn-primary' #messages = render :partial => 'messages', :locals => { :messages => @messages, :subject_length => 130 } \ No newline at end of file diff --git a/app/views/messages/new.haml b/app/views/messages/new.haml index f0a4be8e..8870090a 100644 --- a/app/views/messages/new.haml +++ b/app/views/messages/new.haml @@ -1,4 +1,4 @@ -- content_for :head do +- content_for :javascript do :javascript $(function() { $('#message_recipient_tokens').tokenInput("#{users_path(:format => :json)}", { @@ -6,7 +6,8 @@ prePopulate: $('#message_recipient_tokens').data('pre'), hintText: 'Nach Nutzerin suchen', noResultText: 'Keine Nutzerin gefunden', - searchingText: 'Suche ...' + searchingText: 'Suche ...', + theme: 'facebook' }); $('#message_sent_to_all').click(function() { @@ -38,6 +39,8 @@ = f.input :recipient_tokens, :input_html => { 'data-pre' => User.find_all_by_id(@message.recipients_ids).map { |u| u.token_attributes }.to_json } = f.input :group_id, :as => :select, :collection => Group.order('type DESC, name DESC').all.reject { |g| g.memberships.empty? } = f.input :private - = f.input :subject - = f.input :body - = f.submit \ No newline at end of file + = f.input :subject, input_html: {class: 'input-xxlarge'} + = f.input :body, input_html: {class: 'input-xxlarge'} + .form-actions + = f.submit + = link_to 'oder abbrechen', :back \ No newline at end of file diff --git a/app/views/messages/show.haml b/app/views/messages/show.haml index 0ca92ab4..e991268a 100644 --- a/app/views/messages/show.haml +++ b/app/views/messages/show.haml @@ -16,6 +16,6 @@ %p= simple_format(h(@message.body)) %hr/ %p - = link_to('Antworten', new_message_path(:message => {:reply_to => @message.id})) + = link_to 'Antworten', new_message_path(:message => {:reply_to => @message.id}), class: 'btn' | = link_to 'Nachricht im Überblick', messages_path \ No newline at end of file diff --git a/config/locales/de.yml b/config/locales/de.yml index 9a79cd67..36f8f7d1 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -331,7 +331,7 @@ de: group_id: 'Gruppe' subject: 'Betreff' body: 'Inhalt' - private: 'privat verschicken, Nachricht erscheint nicht im Foodsoft Posteingang' + private: Privat page: body: 'Inhalt' supplier: @@ -394,3 +394,5 @@ de: unit: 'z.B. KG oder 1L oder 500g' stock_article: supplier: '' + message: + private: Nachricht erscheint nicht im Foodsoft Posteingang diff --git a/vendor/assets/javascripts/jquery.tokeninput.js b/vendor/assets/javascripts/jquery.tokeninput.js index 4618d67a..87641a57 100644 --- a/vendor/assets/javascripts/jquery.tokeninput.js +++ b/vendor/assets/javascripts/jquery.tokeninput.js @@ -1,6 +1,6 @@ /* * jQuery Plugin: Tokenizing Autocomplete Text Entry - * Version 1.4.2 + * Version 1.6.0 * * Copyright (c) 2009 James Smith (http://loopj.com) * Licensed jointly under the GPL and MIT licenses, @@ -11,24 +11,46 @@ (function ($) { // Default settings var DEFAULT_SETTINGS = { + // Search settings + method: "GET", + contentType: "json", + queryParam: "q", + searchDelay: 300, + minChars: 1, + propertyToSearch: "name", + jsonContainer: null, + + // Display settings hintText: "Type in a search term", noResultsText: "No results", searchingText: "Searching...", deleteText: "×", - searchDelay: 300, - minChars: 1, + animateDropdown: true, + + // Tokenization settings tokenLimit: null, - jsonContainer: null, - method: "GET", - contentType: "json", - queryParam: "q", tokenDelimiter: ",", preventDuplicates: false, + + // Output settings + tokenValue: "id", + + // Prepopulation settings prePopulate: null, - animateDropdown: true, + processPrePopulate: false, + + // Manipulation settings + idPrefix: "token-input-", + + // Formatters + resultsFormatter: function(item){ return "
  • " + item[this.propertyToSearch]+ "
  • " }, + tokenFormatter: function(item) { return "
  • " + item[this.propertyToSearch] + "

  • " }, + + // Callbacks onResult: null, onAdd: null, - onDelete: null + onDelete: null, + onReady: null }; // Default classes to use when theming @@ -71,17 +93,42 @@ var KEY = { COMMA: 188 }; +// Additional public (exposed) methods +var methods = { + init: function(url_or_data_or_function, options) { + var settings = $.extend({}, DEFAULT_SETTINGS, options || {}); + + return this.each(function () { + $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings)); + }); + }, + clear: function() { + this.data("tokenInputObject").clear(); + return this; + }, + add: function(item) { + this.data("tokenInputObject").add(item); + return this; + }, + remove: function(item) { + this.data("tokenInputObject").remove(item); + return this; + }, + get: function() { + return this.data("tokenInputObject").getTokens(); + } +} // Expose the .tokenInput function to jQuery as a plugin -$.fn.tokenInput = function (url_or_data, options) { - var settings = $.extend({}, DEFAULT_SETTINGS, options || {}); - - return this.each(function () { - new $.TokenList(this, url_or_data, settings); - }); +$.fn.tokenInput = function (method) { + // Method calling and initialization logic + if(methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else { + return methods.init.apply(this, arguments); + } }; - // TokenList class for each input $.TokenList = function (input, url_or_data, settings) { // @@ -89,19 +136,22 @@ $.TokenList = function (input, url_or_data, settings) { // // Configure the data source - if($.type(url_or_data) === "string") { + if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") { // Set the url to query against settings.url = url_or_data; + // If the URL is a function, evaluate it here to do our initalization work + var url = computeURL(); + // Make a smart guess about cross-domain if it wasn't explicitly specified if(settings.crossDomain === undefined) { - if(settings.url.indexOf("://") === -1) { + if(url.indexOf("://") === -1) { settings.crossDomain = false; } else { - settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]); + settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]); } } - } else if($.type(url_or_data) === "array") { + } else if(typeof(url_or_data) === "object") { // Set the local data to search through settings.local_data = url_or_data; } @@ -139,6 +189,7 @@ $.TokenList = function (input, url_or_data, settings) { .css({ outline: "none" }) + .attr("id", settings.idPrefix + input.id) .focus(function () { if (settings.tokenLimit === null || settings.tokenLimit !== token_count) { show_dropdown_hint(); @@ -146,6 +197,7 @@ $.TokenList = function (input, url_or_data, settings) { }) .blur(function () { hide_dropdown(); + $(this).val(""); }) .bind("keyup keydown blur update", resize_input) .keydown(function (event) { @@ -197,6 +249,7 @@ $.TokenList = function (input, url_or_data, settings) { if(!$(this).val().length) { if(selected_token) { delete_token($(selected_token)); + hidden_input.change(); } else if(previous_token.length) { select_token($(previous_token.get(0))); } @@ -215,7 +268,8 @@ $.TokenList = function (input, url_or_data, settings) { case KEY.NUMPAD_ENTER: case KEY.COMMA: if(selected_dropdown_item) { - add_token($(selected_dropdown_item)); + add_token($(selected_dropdown_item).data("tokeninput")); + hidden_input.change(); return false; } break; @@ -246,6 +300,7 @@ $.TokenList = function (input, url_or_data, settings) { // Keep a reference to the selected token and dropdown item var selected_token = null; + var selected_token_index = 0; var selected_dropdown_item = null; // The list to store the token items in @@ -308,19 +363,72 @@ $.TokenList = function (input, url_or_data, settings) { // Pre-populate list if items exist hidden_input.val(""); - li_data = settings.prePopulate || hidden_input.data("pre"); + var li_data = settings.prePopulate || hidden_input.data("pre"); + if(settings.processPrePopulate && $.isFunction(settings.onResult)) { + li_data = settings.onResult.call(hidden_input, li_data); + } if(li_data && li_data.length) { $.each(li_data, function (index, value) { - insert_token(value.id, value.name); + insert_token(value); + checkTokenLimit(); }); } + // Initialization is done + if($.isFunction(settings.onReady)) { + settings.onReady.call(); + } + // + // Public functions + // + + this.clear = function() { + token_list.children("li").each(function() { + if ($(this).children("input").length === 0) { + delete_token($(this)); + } + }); + } + + this.add = function(item) { + add_token(item); + } + + this.remove = function(item) { + token_list.children("li").each(function() { + if ($(this).children("input").length === 0) { + var currToken = $(this).data("tokeninput"); + var match = true; + for (var prop in item) { + if (item[prop] !== currToken[prop]) { + match = false; + break; + } + } + if (match) { + delete_token($(this)); + } + } + }); + } + + this.getTokens = function() { + return saved_tokens; + } // // Private functions // + function checkTokenLimit() { + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { + input_box.hide(); + hide_dropdown(); + return; + } + } + function resize_input() { if(input_val === (input_val = input_box.val())) {return;} @@ -338,8 +446,9 @@ $.TokenList = function (input, url_or_data, settings) { } // Inner function to a token to the list - function insert_token(id, value) { - var this_token = $("
  • "+ value +"

  • ") + function insert_token(item) { + var this_token = settings.tokenFormatter(item); + this_token = $(this_token) .addClass(settings.classes.token) .insertBefore(input_token); @@ -349,30 +458,35 @@ $.TokenList = function (input, url_or_data, settings) { .appendTo(this_token) .click(function () { delete_token($(this).parent()); + hidden_input.change(); return false; }); // Store data on the token - var token_data = {"id": id, "name": value}; - $.data(this_token.get(0), "tokeninput", token_data); + var token_data = {"id": item.id}; + token_data[settings.propertyToSearch] = item[settings.propertyToSearch]; + $.data(this_token.get(0), "tokeninput", item); // Save this token for duplicate checking - saved_tokens.push(token_data); + saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index)); + selected_token_index++; // Update the hidden input - var token_ids = $.map(saved_tokens, function (el) { - return el.id; - }); - hidden_input.val(token_ids.join(settings.tokenDelimiter)); + update_hidden_input(saved_tokens, hidden_input); token_count += 1; + // Check the token limit + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { + input_box.hide(); + hide_dropdown(); + } + return this_token; } // Add a token to the token list based on user input function add_token (item) { - var li_data = $.data(item.get(0), "tokeninput"); var callback = settings.onAdd; // See if the token already exists and select it if we don't want duplicates @@ -381,7 +495,7 @@ $.TokenList = function (input, url_or_data, settings) { token_list.children().each(function () { var existing_token = $(this); var existing_data = $.data(existing_token.get(0), "tokeninput"); - if(existing_data && existing_data.id === li_data.id) { + if(existing_data && existing_data.id === item.id) { found_existing_token = existing_token; return false; } @@ -396,15 +510,9 @@ $.TokenList = function (input, url_or_data, settings) { } // Insert the new tokens - insert_token(li_data.id, li_data.name); - - // Check the token limit - if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { - input_box.hide(); - hide_dropdown(); - return; - } else { - input_box.focus(); + if(settings.tokenLimit == null || token_count < settings.tokenLimit) { + insert_token(item); + checkTokenLimit(); } // Clear input box @@ -415,7 +523,7 @@ $.TokenList = function (input, url_or_data, settings) { // Execute the onAdd callback if defined if($.isFunction(callback)) { - callback(li_data); + callback.call(hidden_input,item); } } @@ -438,10 +546,13 @@ $.TokenList = function (input, url_or_data, settings) { if(position === POSITION.BEFORE) { input_token.insertBefore(token); + selected_token_index--; } else if(position === POSITION.AFTER) { input_token.insertAfter(token); + selected_token_index++; } else { input_token.appendTo(token_list); + selected_token_index = token_count; } // Show the input box and give it focus again @@ -469,6 +580,9 @@ $.TokenList = function (input, url_or_data, settings) { var token_data = $.data(token.get(0), "tokeninput"); var callback = settings.onDelete; + var index = token.prevAll().length; + if(index > selected_token_index) index--; + // Delete the token token.remove(); selected_token = null; @@ -477,15 +591,11 @@ $.TokenList = function (input, url_or_data, settings) { input_box.focus(); // Remove this token from the saved list - saved_tokens = $.grep(saved_tokens, function (val) { - return (val.id !== token_data.id); - }); + saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1)); + if(index < selected_token_index) selected_token_index--; // Update the hidden input - var token_ids = $.map(saved_tokens, function (el) { - return el.id; - }); - hidden_input.val(token_ids.join(settings.tokenDelimiter)); + update_hidden_input(saved_tokens, hidden_input); token_count -= 1; @@ -498,10 +608,19 @@ $.TokenList = function (input, url_or_data, settings) { // Execute the onDelete callback if defined if($.isFunction(callback)) { - callback(token_data); + callback.call(hidden_input,token_data); } } + // Update the hidden input box value + function update_hidden_input(saved_tokens, hidden_input) { + var token_values = $.map(saved_tokens, function (el) { + return el[settings.tokenValue]; + }); + hidden_input.val(token_values.join(settings.tokenDelimiter)); + + } + // Hide and clear the results dropdown function hide_dropdown () { dropdown.hide().empty(); @@ -537,6 +656,10 @@ $.TokenList = function (input, url_or_data, settings) { function highlight_term(value, term) { return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); } + + function find_value_and_highlight_term(template, value, term) { + return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term)); + } // Populate the results dropdown with some results function populate_dropdown (query, results) { @@ -548,15 +671,19 @@ $.TokenList = function (input, url_or_data, settings) { select_dropdown_item($(event.target).closest("li")); }) .mousedown(function (event) { - add_token($(event.target).closest("li")); + add_token($(event.target).closest("li").data("tokeninput")); + hidden_input.change(); return false; }) .hide(); $.each(results, function(index, value) { - var this_li = $("
  • " + highlight_term(value.name, query) + "
  • ") - .appendTo(dropdown_ul); - + var this_li = settings.resultsFormatter(value); + + this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query); + + this_li = $(this_li).appendTo(dropdown_ul); + if(index % 2) { this_li.addClass(settings.classes.dropdownItem); } else { @@ -567,7 +694,7 @@ $.TokenList = function (input, url_or_data, settings) { select_dropdown_item(this_li); } - $.data(this_li.get(0), "tokeninput", {"id": value.id, "name": value.name}); + $.data(this_li.get(0), "tokeninput", value); }); show_dropdown(); @@ -628,17 +755,19 @@ $.TokenList = function (input, url_or_data, settings) { // Do the actual search function run_search(query) { - var cached_results = cache.get(query); + var cache_key = query + computeURL(); + var cached_results = cache.get(cache_key); if(cached_results) { populate_dropdown(query, cached_results); } else { // Are we doing an ajax search or local data search? if(settings.url) { + var url = computeURL(); // Extract exisiting get params var ajax_params = {}; ajax_params.data = {}; - if(settings.url.indexOf("?") > -1) { - var parts = settings.url.split("?"); + if(url.indexOf("?") > -1) { + var parts = url.split("?"); ajax_params.url = parts[0]; var param_array = parts[1].split("&"); @@ -647,7 +776,7 @@ $.TokenList = function (input, url_or_data, settings) { ajax_params.data[kv[0]] = kv[1]; }); } else { - ajax_params.url = settings.url; + ajax_params.url = url; } // Prepare the request @@ -661,9 +790,9 @@ $.TokenList = function (input, url_or_data, settings) { // Attach the success callback ajax_params.success = function(results) { if($.isFunction(settings.onResult)) { - results = settings.onResult.call(this, results); + results = settings.onResult.call(hidden_input, results); } - cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results); + cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results); // only populate the dropdown if the results are associated with the active search query if(input_box.val().toLowerCase() === query) { @@ -676,13 +805,26 @@ $.TokenList = function (input, url_or_data, settings) { } else if(settings.local_data) { // Do the search through local data var results = $.grep(settings.local_data, function (row) { - return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1; + return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1; }); + if($.isFunction(settings.onResult)) { + results = settings.onResult.call(hidden_input, results); + } + cache.add(cache_key, results); populate_dropdown(query, results); } } } + + // compute the dynamic URL + function computeURL() { + var url = settings.url; + if(typeof settings.url == 'function') { + url = settings.url.call(); + } + return url; + } }; // Really basic cache for the results