SickGear/gui/slick/js/lib/jquery.tokeninput.js
echel0n 0d9fbc1ad7 Welcome to our SickBeard-TVRage Edition ...
This version of SickBeard uses both TVDB and TVRage to search and gather it's series data from allowing you to now have access to and download shows that you couldn't before because of being locked into only what TheTVDB had to offer.

Also this edition is based off the code we used in our XEM editon so it does come with scene numbering support as well as all the other features our XEM edition has to offer.

Please before using this with your existing database (sickbeard.db) please make a backup copy of it and delete any other database files such as cache.db and failed.db if present, we HIGHLY recommend starting out with no database files at all to make this a fresh start but the choice is at your own risk!

Enjoy!
2014-03-09 22:39:12 -07:00

861 lines
27 KiB
JavaScript

/*
* jQuery Plugin: Tokenizing Autocomplete Text Entry
* Version 1.6.0
*
* Copyright (c) 2009 James Smith (http://loopj.com)
* Licensed jointly under the GPL and MIT licenses,
* choose which one suits your project best!
*
*/
(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: "×",
animateDropdown: true,
// Tokenization settings
tokenLimit: null,
tokenDelimiter: ",",
preventDuplicates: false,
// Output settings
tokenValue: "id",
// Prepopulation settings
prePopulate: null,
processPrePopulate: true,
// Manipulation settings
idPrefix: "token-input-",
// Formatters
resultsFormatter: function(item){ return "<li><img src='"+sbRoot+"/images/flags/"+item["id"]+".png' /> " + item[this.propertyToSearch]+ "</li>" },
tokenFormatter: function(item) { return "<li><img src='"+sbRoot+"/images/flags/"+item["id"]+".png' /> <p>" + item[this.propertyToSearch] + "</p></li>" },
flag: "flag",
// Callbacks
onResult: null,
onAdd: null,
onDelete: null,
onReady: null
};
// Default classes to use when theming
var DEFAULT_CLASSES = {
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"
};
// Input box position "enum"
var POSITION = {
BEFORE: 0,
AFTER: 1,
END: 2
};
// Keys "enum"
var KEY = {
BACKSPACE: 8,
TAB: 9,
ENTER: 13,
ESCAPE: 27,
SPACE: 32,
PAGE_UP: 33,
PAGE_DOWN: 34,
END: 35,
HOME: 36,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
NUMPAD_ENTER: 108,
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 (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) {
//
// Initialization
//
// Configure the data source
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(url.indexOf("://") === -1) {
settings.crossDomain = false;
} else {
settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
}
}
} else if(typeof(url_or_data) === "object") {
// Set the local data to search through
settings.local_data = url_or_data;
}
// Build class names
if(settings.classes) {
// Use custom class names
settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
} else if(settings.theme) {
// Use theme-suffixed default class names
settings.classes = {};
$.each(DEFAULT_CLASSES, function(key, value) {
settings.classes[key] = value + "-" + settings.theme;
});
} else {
settings.classes = DEFAULT_CLASSES;
}
// 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, old vals
var timeout;
var input_val;
// Create a new text input an attach keyup events
var input_box = $("<input type=\"text\" autocomplete=\"off\" >")
.css({
outline: "none"
})
.attr("id", settings.idPrefix + input.id)
.focus(function () {
if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
show_dropdown_hint();
}
})
.blur(function () {
hide_dropdown();
$(this).val("");
})
.bind("keyup keydown blur update", resize_input)
.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));
hidden_input.change();
} 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();}, 5);
}
break;
case KEY.TAB:
case KEY.ENTER:
case KEY.NUMPAD_ENTER:
case KEY.COMMA:
if(selected_dropdown_item) {
add_token($(selected_dropdown_item).data("tokeninput"));
hidden_input.change();
return false;
}
break;
case KEY.ESCAPE:
hide_dropdown();
return true;
default:
if(String.fromCharCode(event.which)) {
// set a timeout just long enough to let this function finish.
setTimeout(function(){do_search();}, 5);
}
break;
}
});
// Keep a reference to the original input box
var hidden_input = $(input)
.hide()
.val("")
.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_token_index = 0;
var selected_dropdown_item = null;
// The list to store the token items in
var token_list = $("<ul />")
.addClass(settings.classes.tokenList)
.click(function (event) {
var li = $(event.target).closest("li");
if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
toggle_select_token(li);
} else {
// Deselect selected token
if(selected_token) {
deselect_token($(selected_token), POSITION.END);
}
// Focus input box
input_box.focus();
}
})
.mouseover(function (event) {
var li = $(event.target).closest("li");
if(li && selected_token !== this) {
li.addClass(settings.classes.highlightedToken);
}
})
.mouseout(function (event) {
var li = $(event.target).closest("li");
if(li && selected_token !== this) {
li.removeClass(settings.classes.highlightedToken);
}
})
.insertBefore(hidden_input);
// The token holding the input box
var input_token = $("<li />")
.addClass(settings.classes.inputToken)
.appendTo(token_list)
.append(input_box);
// The list to store the dropdown items in
var dropdown = $("<div>")
.addClass(settings.classes.dropdown)
.appendTo("body")
.hide();
// Magic element to help us resize the text input
var input_resizer = $("<tester/>")
.insertAfter(input_box)
.css({
position: "absolute",
top: -9999,
left: -9999,
width: "auto",
fontSize: input_box.css("fontSize"),
fontFamily: input_box.css("fontFamily"),
fontWeight: input_box.css("fontWeight"),
letterSpacing: input_box.css("letterSpacing"),
whiteSpace: "nowrap"
});
// Pre-populate list if items exist
hidden_input.val("");
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);
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;}
// Enter new content into resizer and resize input accordingly
var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
input_resizer.html(escaped);
input_box.width(input_resizer.width() + 30);
}
function is_printable_character(keycode) {
return ((keycode >= 48 && keycode <= 90) || // 0-1a-z
(keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * .
(keycode >= 186 && keycode <= 192) || // ; = , - . / ^
(keycode >= 219 && keycode <= 222)); // ( \ ) '
}
// Inner function to a token to the list
function insert_token(item) {
var this_token = settings.tokenFormatter(item);
this_token = $(this_token)
.addClass(settings.classes.token)
.insertBefore(input_token);
// The 'delete token' button
$("<span>" + settings.deleteText + "</span>")
.addClass(settings.classes.tokenDelete)
.appendTo(this_token)
.click(function () {
delete_token($(this).parent());
hidden_input.change();
return false;
});
// Store data on the token
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 = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
selected_token_index++;
// Update the hidden input
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 callback = settings.onAdd;
// See if the token already exists and select it if we don't want duplicates
if(token_count > 0 && settings.preventDuplicates) {
var found_existing_token = null;
token_list.children().each(function () {
var existing_token = $(this);
var existing_data = $.data(existing_token.get(0), "tokeninput");
if(existing_data && existing_data.id === item.id) {
found_existing_token = existing_token;
return false;
}
});
if(found_existing_token) {
select_token(found_existing_token);
input_token.insertAfter(found_existing_token);
input_box.focus();
return;
}
}
// Insert the new tokens
if(settings.tokenLimit == null || token_count < settings.tokenLimit) {
insert_token(item);
checkTokenLimit();
}
// Clear input box
input_box.val("");
// Don't show the help dropdown, they've got the idea
hide_dropdown();
// Execute the onAdd callback if defined
if($.isFunction(callback)) {
callback.call(hidden_input,item);
}
}
// 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);
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
input_box.focus();
}
// Toggle selection of a token in the token list
function toggle_select_token(token) {
var previous_selected_token = selected_token;
if(selected_token) {
deselect_token($(selected_token), POSITION.END);
}
if(previous_selected_token === token.get(0)) {
deselect_token(token, POSITION.END);
} else {
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");
var callback = settings.onDelete;
var index = token.prevAll().length;
if(index > selected_token_index) index--;
// Delete the token
token.remove();
selected_token = null;
// Show the input box and give it focus again
input_box.focus();
// Remove this token from the saved list
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
update_hidden_input(saved_tokens, hidden_input);
token_count -= 1;
if(settings.tokenLimit !== null) {
input_box
.show()
.val("")
.focus();
}
// Execute the onDelete callback if defined
if($.isFunction(callback)) {
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();
selected_dropdown_item = null;
}
function show_dropdown() {
dropdown
.css({
position: "absolute",
top: $(token_list).offset().top + $(token_list).outerHeight(),
left: $(token_list).offset().left,
zindex: 999
})
.show();
}
function show_dropdown_searching () {
if(settings.searchingText) {
dropdown.html("<p>"+settings.searchingText+"</p>");
show_dropdown();
}
}
function show_dropdown_hint () {
if(settings.hintText) {
dropdown.html("<p>"+settings.hintText+"</p>");
show_dropdown();
}
}
// Highlight the query part of the search term
function highlight_term(value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
}
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) {
if(results && results.length) {
dropdown.empty();
var dropdown_ul = $("<ul>")
.appendTo(dropdown)
.mouseover(function (event) {
select_dropdown_item($(event.target).closest("li"));
})
.mousedown(function (event) {
add_token($(event.target).closest("li").data("tokeninput"));
hidden_input.change();
return false;
})
.hide();
$.each(results, function(index, value) {
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 {
this_li.addClass(settings.classes.dropdownItem2);
}
if(index === 0) {
select_dropdown_item(this_li);
}
$.data(this_li.get(0), "tokeninput", value);
});
show_dropdown();
if(settings.animateDropdown) {
dropdown_ul.slideDown("fast");
} else {
dropdown_ul.show();
}
} else {
if(settings.noResultsText) {
dropdown.html("<p>"+settings.noResultsText+"</p>");
show_dropdown();
}
}
}
// 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() {
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();
clearTimeout(timeout);
timeout = setTimeout(function(){
run_search(query);
}, settings.searchDelay);
} else {
hide_dropdown();
}
}
}
// Do the actual search
function run_search(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(url.indexOf("?") > -1) {
var parts = url.split("?");
ajax_params.url = parts[0];
var param_array = parts[1].split("&");
$.each(param_array, function (index, value) {
var kv = value.split("=");
ajax_params.data[kv[0]] = kv[1];
});
} else {
ajax_params.url = url;
}
// Prepare the request
ajax_params.data[settings.queryParam] = query;
ajax_params.type = settings.method;
ajax_params.dataType = settings.contentType;
if(settings.crossDomain) {
ajax_params.dataType = "jsonp";
}
// Attach the success callback
ajax_params.success = function(results) {
if($.isFunction(settings.onResult)) {
results = settings.onResult.call(hidden_input, 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) {
populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
}
};
// Make the request
$.ajax(ajax_params);
} else if(settings.local_data) {
// Do the search through local data
var results = $.grep(settings.local_data, function (row) {
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
$.TokenList.Cache = function (options) {
var settings = $.extend({
max_size: 500
}, 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 += 1;
}
data[query] = results;
};
this.get = function (query) {
return data[query];
};
};
}(jQuery));