moved media to media folder in app
[~kgodey/maayanwich.git] / media / jquery.tokeninput.js
diff --git a/media/jquery.tokeninput.js b/media/jquery.tokeninput.js
new file mode 100755 (executable)
index 0000000..c804e49
--- /dev/null
@@ -0,0 +1,594 @@
+/*
+ * jQuery Plugin: Tokenizing Autocomplete Text Entry
+ * Version 1.1
+ *
+ * Copyright (c) 2009 James Smith (http://loopj.com)
+ * Licensed jointly under the GPL and MIT licenses,
+ * choose which one suits your project best!
+ *
+ */
+
+(function($) {
+
+$.fn.tokenInput = function (url, options) {
+    var settings = $.extend({
+        url: url,
+        hintText: "Type in a search term",
+        noResultsText: "No results",
+        searchingText: "Searching...",
+        searchDelay: 300,
+        minChars: 1,
+        tokenLimit: null,
+        jsonContainer: null,
+        method: "GET",
+        contentType: "json",
+        queryParam: "q",
+        onResult: null
+    }, options);
+
+    settings.classes = $.extend({
+        tokenList: "token-input-list",
+        token: "token-input-token",
+        tokenDelete: "token-input-delete-token",
+        selectedToken: "token-input-selected-token",
+        highlightedToken: "token-input-highlighted-token",
+        dropdown: "token-input-dropdown",
+        dropdownItem: "token-input-dropdown-item",
+        dropdownItem2: "token-input-dropdown-item2",
+        selectedDropdownItem: "token-input-selected-dropdown-item",
+        inputToken: "token-input-input-token"
+    }, options.classes);
+
+    return this.each(function () {
+        var list = new $.TokenList(this, settings);
+    });
+};
+
+$.TokenList = function (input, settings) {
+    //
+    // Variables
+    //
+
+    // Input box position "enum"
+    var POSITION = {
+        BEFORE: 0,
+        AFTER: 1,
+        END: 2
+    };
+
+    // Keys "enum"
+    var KEY = {
+        BACKSPACE: 8,
+        TAB: 9,
+        RETURN: 13,
+        ESC: 27,
+        LEFT: 37,
+        UP: 38,
+        RIGHT: 39,
+        DOWN: 40,
+        COMMA: 188
+    };
+
+    // Save the tokens
+    var saved_tokens = [];
+    
+    // Keep track of the number of tokens in the list
+    var token_count = 0;
+
+    // Basic cache to save on db hits
+    var cache = new $.TokenList.Cache();
+
+    // Keep track of the timeout
+    var timeout;
+
+    // Create a new text input an attach keyup events
+    var input_box = $("<input type=\"text\">")
+        .css({
+            outline: "none"
+        })
+        .focus(function () {
+            if (settings.tokenLimit == null || settings.tokenLimit != token_count) {
+                show_dropdown_hint();
+            }
+        })
+        .blur(function () {
+            hide_dropdown();
+        })
+        .keydown(function (event) {
+            var previous_token;
+            var next_token;
+
+            switch(event.keyCode) {
+                case KEY.LEFT:
+                case KEY.RIGHT:
+                case KEY.UP:
+                case KEY.DOWN:
+                    if(!$(this).val()) {
+                        previous_token = input_token.prev();
+                        next_token = input_token.next();
+
+                        if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
+                            // Check if there is a previous/next token and it is selected
+                            if(event.keyCode == KEY.LEFT || event.keyCode == KEY.UP) {
+                                deselect_token($(selected_token), POSITION.BEFORE);
+                            } else {
+                                deselect_token($(selected_token), POSITION.AFTER);
+                            }
+                        } else if((event.keyCode == KEY.LEFT || event.keyCode == KEY.UP) && previous_token.length) {
+                            // We are moving left, select the previous token if it exists
+                            select_token($(previous_token.get(0)));
+                        } else if((event.keyCode == KEY.RIGHT || event.keyCode == KEY.DOWN) && next_token.length) {
+                            // We are moving right, select the next token if it exists
+                            select_token($(next_token.get(0)));
+                        }
+                    } else {
+                        var dropdown_item = null;
+
+                        if(event.keyCode == KEY.DOWN || event.keyCode == KEY.RIGHT) {
+                            dropdown_item = $(selected_dropdown_item).next();
+                        } else {
+                            dropdown_item = $(selected_dropdown_item).prev();
+                        }
+
+                        if(dropdown_item.length) {
+                            select_dropdown_item(dropdown_item);
+                        }
+                        return false;
+                    }
+                    break;
+
+                case KEY.BACKSPACE:
+                    previous_token = input_token.prev();
+
+                    if(!$(this).val().length) {
+                        if(selected_token) {
+                            delete_token($(selected_token));
+                        } else if(previous_token.length) {
+                            select_token($(previous_token.get(0)));
+                        }
+
+                        return false;
+                    } else if($(this).val().length == 1) {
+                        hide_dropdown();
+                    } else {
+                        // set a timeout just long enough to let this function finish.
+                        setTimeout(function(){do_search(false);}, 5);
+                    }
+                    break;
+
+                case KEY.TAB:
+                case KEY.RETURN:
+                case KEY.COMMA:
+                  if(selected_dropdown_item) {
+                    add_token($(selected_dropdown_item));
+                    return false;
+                  }
+                  break;
+
+                case KEY.ESC:
+                  hide_dropdown();
+                  return true;
+
+                default:
+                    if(is_printable_character(event.keyCode)) {
+                      // set a timeout just long enough to let this function finish.
+                      setTimeout(function(){do_search(false);}, 5);
+                    }
+                    break;
+            }
+        });
+
+    // Keep a reference to the original input box
+    var hidden_input = $(input)
+                           .hide()
+                           .focus(function () {
+                               input_box.focus();
+                           })
+                           .blur(function () {
+                               input_box.blur();
+                           });
+
+    // Keep a reference to the selected token and dropdown item
+    var selected_token = null;
+    var selected_dropdown_item = null;
+
+    // The list to store the token items in
+    var token_list = $("<ul />")
+        .addClass(settings.classes.tokenList)
+        .insertAfter(hidden_input)
+        .click(function (event) {
+            var li = get_element_from_event(event, "li");
+            if(li && li.get(0) != input_token.get(0)) {
+                toggle_select_token(li);
+                return false;
+            } else {
+                input_box.focus();
+
+                if(selected_token) {
+                    deselect_token($(selected_token), POSITION.END);
+                }
+            }
+        })
+        .mouseover(function (event) {
+            var li = get_element_from_event(event, "li");
+            if(li && selected_token !== this) {
+                li.addClass(settings.classes.highlightedToken);
+            }
+        })
+        .mouseout(function (event) {
+            var li = get_element_from_event(event, "li");
+            if(li && selected_token !== this) {
+                li.removeClass(settings.classes.highlightedToken);
+            }
+        })
+        .mousedown(function (event) {
+            // Stop user selecting text on tokens
+            var li = get_element_from_event(event, "li");
+            if(li){
+                return false;
+            }
+        });
+
+
+    // The list to store the dropdown items in
+    var dropdown = $("<div>")
+        .addClass(settings.classes.dropdown)
+        .insertAfter(token_list)
+        .hide();
+
+    // The token holding the input box
+    var input_token = $("<li />")
+        .addClass(settings.classes.inputToken)
+        .appendTo(token_list)
+        .append(input_box);
+
+    init_list();
+
+    //
+    // Functions
+    //
+
+
+    // Pre-populate list if items exist
+    function init_list () {
+        li_data = settings.prePopulate;
+        if(li_data && li_data.length) {
+            for(var i in li_data) {
+                var this_token = $("<li><p>"+li_data[i].name+"</p> </li>")
+                    .addClass(settings.classes.token)
+                    .insertBefore(input_token);
+
+                $("<span>x</span>")
+                    .addClass(settings.classes.tokenDelete)
+                    .appendTo(this_token)
+                    .click(function () {
+                        delete_token($(this).parent());
+                        return false;
+                    });
+
+                $.data(this_token.get(0), "tokeninput", {"id": li_data[i].id, "name": li_data[i].name});
+
+                // Clear input box and make sure it keeps focus
+                input_box
+                    .val("")
+                    .focus();
+
+                // Don't show the help dropdown, they've got the idea
+                hide_dropdown();
+
+                // Save this token id
+                var id_string = li_data[i].id + ","
+                hidden_input.val(hidden_input.val() + id_string);
+            }
+        }
+    }
+
+    function is_printable_character(keycode) {
+        if((keycode >= 48 && keycode <= 90) ||      // 0-1a-z
+           (keycode >= 96 && keycode <= 111) ||     // numpad 0-9 + - / * .
+           (keycode >= 186 && keycode <= 192) ||    // ; = , - . / ^
+           (keycode >= 219 && keycode <= 222)       // ( \ ) '
+          ) {
+              return true;
+          } else {
+              return false;
+          }
+    }
+
+    // Get an element of a particular type from an event (click/mouseover etc)
+    function get_element_from_event (event, element_type) {
+        var target = $(event.target);
+        var element = null;
+
+        if(target.is(element_type)) {
+            element = target;
+        } else if(target.parent(element_type).length) {
+            element = target.parent(element_type+":first");
+        }
+
+        return element;
+    }
+
+    // Inner function to a token to the list
+    function insert_token(id, value) {
+      var this_token = $("<li><p>"+ value +"</p> </li>")
+      .addClass(settings.classes.token)
+      .insertBefore(input_token);
+
+      // The 'delete token' button
+      $("<span>x</span>")
+          .addClass(settings.classes.tokenDelete)
+          .appendTo(this_token)
+          .click(function () {
+              delete_token($(this).parent());
+              return false;
+          });
+
+      $.data(this_token.get(0), "tokeninput", {"id": id, "name": value});
+
+      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 this_token = insert_token(li_data.id, li_data.name);
+
+        // Clear input box and make sure it keeps focus
+        input_box
+            .val("")
+            .focus();
+
+        // Don't show the help dropdown, they've got the idea
+        hide_dropdown();
+
+        // Save this token id
+        var id_string = li_data.id + ","
+        hidden_input.val(hidden_input.val() + id_string);
+        
+        token_count++;
+        
+        if(settings.tokenLimit != null && settings.tokenLimit >= token_count) {
+            input_box.hide();
+            hide_dropdown();
+        }
+    }
+
+    // Select a token in the token list
+    function select_token (token) {
+        token.addClass(settings.classes.selectedToken);
+        selected_token = token.get(0);
+
+        // Hide input box
+        input_box.val("");
+
+        // Hide dropdown if it is visible (eg if we clicked to select token)
+        hide_dropdown();
+    }
+
+    // Deselect a token in the token list
+    function deselect_token (token, position) {
+        token.removeClass(settings.classes.selectedToken);
+        selected_token = null;
+
+        if(position == POSITION.BEFORE) {
+            input_token.insertBefore(token);
+        } else if(position == POSITION.AFTER) {
+            input_token.insertAfter(token);
+        } else {
+            input_token.appendTo(token_list);
+        }
+
+        // Show the input box and give it focus again
+        input_box.focus();
+    }
+
+    // Toggle selection of a token in the token list
+    function toggle_select_token (token) {
+        if(selected_token == token.get(0)) {
+            deselect_token(token, POSITION.END);
+        } else {
+            if(selected_token) {
+                deselect_token($(selected_token), POSITION.END);
+            }
+            select_token(token);
+        }
+    }
+
+    // Delete a token from the token list
+    function delete_token (token) {
+        // Remove the id from the saved list
+        var token_data = $.data(token.get(0), "tokeninput");
+
+        // Delete the token
+        token.remove();
+        selected_token = null;
+
+        // Show the input box and give it focus again
+        input_box.focus();
+
+        // Delete this token's id from hidden input
+        var str = hidden_input.val()
+        var start = str.indexOf(token_data.id+",");
+        var end = str.indexOf(",", start) + 1;
+
+        if(end >= str.length) {
+            hidden_input.val(str.slice(0, start));
+        } else {
+            hidden_input.val(str.slice(0, start) + str.slice(end, str.length));
+        }
+        
+        token_count--;
+        
+        if (settings.tokenLimit != null) {
+            input_box
+                .show()
+                .val("")
+                .focus();
+        }
+    }
+
+    // Hide and clear the results dropdown
+    function hide_dropdown () {
+        dropdown.hide().empty();
+        selected_dropdown_item = null;
+    }
+
+    function show_dropdown_searching () {
+        dropdown
+            .html("<p>"+settings.searchingText+"</p>")
+            .show();
+    }
+
+    function show_dropdown_hint () {
+        dropdown
+            .html("<p>"+settings.hintText+"</p>")
+            .show();
+    }
+
+    // Highlight the query part of the search term
+       function highlight_term(value, term) {
+               return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
+       }
+
+    // Populate the results dropdown with some results
+    function populate_dropdown (query, results) {
+        if(results.length) {
+            dropdown.empty();
+            var dropdown_ul = $("<ul>")
+                .appendTo(dropdown)
+                .mouseover(function (event) {
+                    select_dropdown_item(get_element_from_event(event, "li"));
+                })
+                .mousedown(function (event) {
+                    add_token(get_element_from_event(event, "li"));
+                    return false;
+                })
+                .hide();
+
+            for(var i in results) {
+                if (results.hasOwnProperty(i)) {
+                    var this_li = $("<li>"+highlight_term(results[i].name, query)+"</li>")
+                                      .appendTo(dropdown_ul);
+
+                    if(i%2) {
+                        this_li.addClass(settings.classes.dropdownItem);
+                    } else {
+                        this_li.addClass(settings.classes.dropdownItem2);
+                    }
+
+                    if(i == 0) {
+                        select_dropdown_item(this_li);
+                    }
+
+                    $.data(this_li.get(0), "tokeninput", {"id": results[i].id, "name": results[i].name});
+                }
+            }
+
+            dropdown.show();
+            dropdown_ul.slideDown("fast");
+
+        } else {
+            dropdown
+                .html("<p>"+settings.noResultsText+"</p>")
+                .show();
+        }
+    }
+
+    // Highlight an item in the results dropdown
+    function select_dropdown_item (item) {
+        if(item) {
+            if(selected_dropdown_item) {
+                deselect_dropdown_item($(selected_dropdown_item));
+            }
+
+            item.addClass(settings.classes.selectedDropdownItem);
+            selected_dropdown_item = item.get(0);
+        }
+    }
+
+    // Remove highlighting from an item in the results dropdown
+    function deselect_dropdown_item (item) {
+        item.removeClass(settings.classes.selectedDropdownItem);
+        selected_dropdown_item = null;
+    }
+
+    // Do a search and show the "searching" dropdown if the input is longer
+    // than settings.minChars
+    function do_search(immediate) {
+        var query = input_box.val().toLowerCase();
+
+        if (query && query.length) {
+            if(selected_token) {
+                deselect_token($(selected_token), POSITION.AFTER);
+            }
+            if (query.length >= settings.minChars) {
+                show_dropdown_searching();
+                if (immediate) {
+                    run_search(query);
+                } else {
+                    clearTimeout(timeout);
+                    timeout = setTimeout(function(){run_search(query);}, settings.searchDelay);
+                }
+            } else {
+                hide_dropdown();
+            }
+        }
+    }
+
+    // Do the actual search
+    function run_search(query) {
+        var cached_results = cache.get(query);
+        if(cached_results) {
+            populate_dropdown(query, cached_results);
+        } else {
+                       var queryStringDelimiter = settings.url.indexOf("?") < 0 ? "?" : "&";
+                       var callback = function(results) {
+                         if($.isFunction(settings.onResult)) {
+                             results = settings.onResult.call(this, results);
+                         }
+              cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
+              populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
+            };
+            
+            if(settings.method == "POST") {
+                           $.post(settings.url + queryStringDelimiter + settings.queryParam + "=" + query, {}, callback, settings.contentType);
+                   } else {
+                       $.get(settings.url + queryStringDelimiter + settings.queryParam + "=" + query, {}, callback, settings.contentType);
+                   }
+        }
+    }
+};
+
+// Really basic cache for the results
+$.TokenList.Cache = function (options) {
+    var settings = $.extend({
+        max_size: 50
+    }, options);
+
+    var data = {};
+    var size = 0;
+
+    var flush = function () {
+        data = {};
+        size = 0;
+    };
+
+    this.add = function (query, results) {
+        if(size > settings.max_size) {
+            flush();
+        }
+
+        if(!data[query]) {
+            size++;
+        }
+
+        data[query] = results;
+    };
+
+    this.get = function (query) {
+        return data[query];
+    };
+};
+
+})(jQuery);
\ No newline at end of file