aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--admin.php13
-rw-r--r--admin/batch_manager.php12
-rw-r--r--admin/batch_manager_global.php2
-rw-r--r--admin/batch_manager_unit.php4
-rw-r--r--admin/include/functions.php15
-rw-r--r--admin/picture_modify.php13
-rw-r--r--admin/themes/default/template/batch_manager_global.tpl32
-rw-r--r--admin/themes/default/template/batch_manager_unit.tpl38
-rw-r--r--admin/themes/default/template/picture_modify.tpl34
-rw-r--r--admin/themes/default/theme.css62
-rw-r--r--admin/themes/roma/theme.css7
-rw-r--r--language/en_UK/admin.lang.php4
-rw-r--r--language/fr_FR/admin.lang.php4
-rw-r--r--themes/default/js/plugins/jquery.fcbkcomplete.js645
-rw-r--r--themes/default/js/plugins/jquery.tokeninput.js777
15 files changed, 901 insertions, 761 deletions
diff --git a/admin.php b/admin.php
index 9c6a6e5f1..43a0d37e0 100644
--- a/admin.php
+++ b/admin.php
@@ -44,19 +44,6 @@ check_status(ACCESS_ADMINISTRATOR);
// | Direct actions |
// +-----------------------------------------------------------------------+
-// tags
-if (isset($_GET['fckb_tags']))
-{
- $query = '
-SELECT
- id AS tag_id,
- name AS tag_name
- FROM '.TAGS_TABLE.'
-;';
- echo json_encode(get_fckb_taglist($query));
- exit();
-}
-
// theme changer
if (isset($_GET['change_theme']))
{
diff --git a/admin/batch_manager.php b/admin/batch_manager.php
index b333ca9ac..c1d3687d4 100644
--- a/admin/batch_manager.php
+++ b/admin/batch_manager.php
@@ -361,6 +361,18 @@ if (in_array($page['tab'], $tab_codes))
$tabsheet->assign();
// +-----------------------------------------------------------------------+
+// | tags |
+// +-----------------------------------------------------------------------+
+
+$query = '
+SELECT
+ id AS tag_id,
+ name AS tag_name
+ FROM '.TAGS_TABLE.'
+;';
+$template->assign('tags', get_taglist($query));
+
+// +-----------------------------------------------------------------------+
// | open specific mode |
// +-----------------------------------------------------------------------+
diff --git a/admin/batch_manager_global.php b/admin/batch_manager_global.php
index 11cafe02d..73d11be2d 100644
--- a/admin/batch_manager_global.php
+++ b/admin/batch_manager_global.php
@@ -123,7 +123,7 @@ DELETE
}
else
{
- $tag_ids = get_fckb_tag_ids($_POST['add_tags']);
+ $tag_ids = get_tag_ids($_POST['add_tags']);
add_tags($tag_ids, $collection);
if ('with no tag' == $page['prefilter'])
diff --git a/admin/batch_manager_unit.php b/admin/batch_manager_unit.php
index aa28b856e..b7ca074d7 100644
--- a/admin/batch_manager_unit.php
+++ b/admin/batch_manager_unit.php
@@ -108,7 +108,7 @@ SELECT id, date_creation
// tags management
if (isset($_POST[ 'tags-'.$row['id'] ]))
{
- $tag_ids = get_fckb_tag_ids($_POST[ 'tags-'.$row['id'] ]);
+ $tag_ids = get_tag_ids($_POST[ 'tags-'.$row['id'] ]);
set_tags($tag_ids, $row['id']);
}
}
@@ -256,7 +256,7 @@ SELECT
JOIN '.TAGS_TABLE.' AS t ON t.id = it.tag_id
WHERE image_id = '.$row['id'].'
;';
- $tag_selection = get_fckb_taglist($query);
+ $tag_selection = get_taglist($query);
$template->append(
'elements',
diff --git a/admin/include/functions.php b/admin/include/functions.php
index f5afa9633..c929d2c55 100644
--- a/admin/include/functions.php
+++ b/admin/include/functions.php
@@ -2100,25 +2100,29 @@ function get_active_menu($menu_page)
return 0;
}
-function get_fckb_taglist($query)
+function get_taglist($query)
{
$result = pwg_query($query);
+
$taglist = array();
while ($row = pwg_db_fetch_assoc($result))
{
array_push(
$taglist,
array(
- 'key' => $row['tag_name'],
- 'value' => '~~'.$row['tag_id'].'~~',
+ 'name' => $row['tag_name'],
+ 'id' => '~~'.$row['tag_id'].'~~',
)
);
}
-
+
+ $cmp = create_function('$a,$b', 'return strcasecmp($a["name"], $b["name"]);');
+ usort($taglist, $cmp);
+
return $taglist;
}
-function get_fckb_tag_ids($raw_tags)
+function get_tag_ids($raw_tags)
{
// In $raw_tags we receive something like array('~~6~~', '~~59~~', 'New
// tag', 'Another new tag') The ~~34~~ means that it is an existing
@@ -2126,6 +2130,7 @@ function get_fckb_tag_ids($raw_tags)
// or "1234" (numeric characters only)
$tag_ids = array();
+ $raw_tags = explode(',',$raw_tags);
foreach ($raw_tags as $raw_tag)
{
diff --git a/admin/picture_modify.php b/admin/picture_modify.php
index 333bf3e88..97bff6e5e 100644
--- a/admin/picture_modify.php
+++ b/admin/picture_modify.php
@@ -166,7 +166,7 @@ if (isset($_POST['submit']) and count($page['errors']) == 0)
$tag_ids = array();
if (isset($_POST['tags']))
{
- $tag_ids = get_fckb_tag_ids($_POST['tags']);
+ $tag_ids = get_tag_ids($_POST['tags']);
}
set_tags($tag_ids, $_GET['image_id']);
@@ -233,7 +233,15 @@ SELECT
JOIN '.TAGS_TABLE.' AS t ON t.id = it.tag_id
WHERE image_id = '.$_GET['image_id'].'
;';
-$tags = get_fckb_taglist($query);
+$tag_selection = get_taglist($query);
+
+$query = '
+SELECT
+ id AS tag_id,
+ name AS tag_name
+ FROM '.TAGS_TABLE.'
+;';
+$tags = get_taglist($query);
// retrieving direct information about picture
$query = '
@@ -267,6 +275,7 @@ $admin_url_start.= isset($_GET['cat_id']) ? '&cat_id='.$_GET['cat_id'] : '';
$template->assign(
array(
+ 'tag_selection' => $tag_selection,
'tags' => $tags,
'U_SYNC' => $admin_url_start.'&sync_metadata=1',
'U_DELETE' => $admin_url_start.'&delete=1&pwg_token='.get_pwg_token(),
diff --git a/admin/themes/default/template/batch_manager_global.tpl b/admin/themes/default/template/batch_manager_global.tpl
index cadd7ce13..b01a120a1 100644
--- a/admin/themes/default/template/batch_manager_global.tpl
+++ b/admin/themes/default/template/batch_manager_global.tpl
@@ -5,22 +5,24 @@
pwg_initialization_datepicker("#date_creation_day", "#date_creation_month", "#date_creation_year", "#date_creation_linked_date", "#date_creation_action_set");
{/literal}{/footer_script}
-{combine_script id='jquery.fcbkcomplete' load='footer' require='jquery' path='themes/default/js/plugins/jquery.fcbkcomplete.js'}
-
-{footer_script require='jquery.fcbkcomplete'}{literal}
-jQuery(document).ready(function() {
- jQuery("#tags").fcbkcomplete({
- json_url: "admin.php?fckb_tags=1",
- cache: false,
- filter_case: false,
- filter_hide: true,
- firstselected: true,
- filter_selected: true,
- maxitems: 100,
- newel: true
- });
+{combine_script id='jquery.tokeninput' load='footer' require='jquery' path='themes/default/js/plugins/jquery.tokeninput.js'}
+
+{footer_script require='jquery.tokeninput'}
+jQuery(document).ready(function() {ldelim}
+ jQuery("#tags").tokenInput(
+ [{foreach from=$tags item=tag name=tags}{ldelim}"name":"{$tag.name}","id":"{$tag.id}"{rdelim}{if !$smarty.foreach.tags.last},{/if}{/foreach}],
+ {ldelim}
+ hintText: '{'Type in a search term'|@translate}',
+ noResultsText: '{'No results'|@translate}',
+ searchingText: '{'Searching...'|@translate}',
+ newText: ' ({'new'|@translate})',
+ animateDropdown: false,
+ preventDuplicates: true,
+ allowCreation: true
+ }
+ );
});
-{/literal}{/footer_script}
+{/footer_script}
{footer_script}
var nb_thumbs_page = {$nb_thumbs_page};
diff --git a/admin/themes/default/template/batch_manager_unit.tpl b/admin/themes/default/template/batch_manager_unit.tpl
index 9a1dde834..66b8b6b17 100644
--- a/admin/themes/default/template/batch_manager_unit.tpl
+++ b/admin/themes/default/template/batch_manager_unit.tpl
@@ -2,8 +2,8 @@
{include file='include/datepicker.inc.tpl'}
{include file='include/colorbox.inc.tpl'}
-{combine_script id='jquery.fcbkcomplete' load='async' require='jquery' path='themes/default/js/plugins/jquery.fcbkcomplete.js'}
-{footer_script require='jquery.fcbkcomplete'}
+{combine_script id='jquery.tokeninput' load='async' require='jquery' path='themes/default/js/plugins/jquery.tokeninput.js'}
+{footer_script require='jquery.tokeninput'}
var tag_boxes_selector = "";
{foreach from=$elements item=element name=element}
{if $smarty.foreach.element.first}
@@ -13,22 +13,24 @@ prefix = ", ";
{/if}
tag_boxes_selector = tag_boxes_selector + prefix + "#tags-" + {$element.ID};
{/foreach}
-{literal}
-jQuery(document).ready(function() {
- $(tag_boxes_selector).fcbkcomplete({
- json_url: "admin.php?fckb_tags=1",
- cache: false,
- filter_case: false,
- filter_hide: true,
- firstselected: true,
- filter_selected: true,
- maxitems: 100,
- newel: true
- });
-
- $("a.preview-box").colorbox();
+
+jQuery(document).ready(function() {ldelim}
+ jQuery(tag_boxes_selector).tokenInput(
+ [{foreach from=$tags item=tag name=tags}{ldelim}"name":"{$tag.name}","id":"{$tag.id}"{rdelim}{if !$smarty.foreach.tags.last},{/if}{/foreach}],
+ {ldelim}
+ hintText: '{'Type in a search term'|@translate}',
+ noResultsText: '{'No results'|@translate}',
+ searchingText: '{'Searching...'|@translate}',
+ newText: ' ({'new'|@translate})',
+ animateDropdown: false,
+ preventDuplicates: true,
+ allowCreation: true
+ }
+ );
+
+ jQuery("a.preview-box").colorbox();
});
-{/literal}{/footer_script}
+{/footer_script}
<h2>{'Batch Manager'|@translate}</h2>
@@ -112,7 +114,7 @@ jQuery(document).ready(function() {
<select id="tags-{$element.ID}" name="tags-{$element.ID}">
{foreach from=$element.TAGS item=tag}
- <option value="{$tag.value}" class="selected">{$tag.key}</option>
+ <option value="{$tag.id}" class="selected">{$tag.name}</option>
{/foreach}
</select>
diff --git a/admin/themes/default/template/picture_modify.tpl b/admin/themes/default/template/picture_modify.tpl
index a5244f240..8cc01c43a 100644
--- a/admin/themes/default/template/picture_modify.tpl
+++ b/admin/themes/default/template/picture_modify.tpl
@@ -2,21 +2,23 @@
{include file='include/dbselect.inc.tpl'}
{include file='include/datepicker.inc.tpl'}
-{combine_script id='jquery.fcbkcomplete' load='async' require='jquery' path='themes/default/js/plugins/jquery.fcbkcomplete.js'}
-{footer_script require='jquery.fcbkcomplete'}{literal}
-jQuery(document).ready(function() {
- jQuery("#tags").fcbkcomplete({
- json_url: "admin.php?fckb_tags=1",
- cache: false,
- filter_case: false,
- filter_hide: true,
- firstselected: true,
- filter_selected: true,
- maxitems: 100,
- newel: true
- });
+{combine_script id='jquery.tokeninput' load='async' require='jquery' path='themes/default/js/plugins/jquery.tokeninput.js'}
+{footer_script require='jquery.tokeninput'}
+jQuery(document).ready(function() {ldelim}
+ jQuery("#tags").tokenInput(
+ [{foreach from=$tags item=tag name=tags}{ldelim}"name":"{$tag.name}","id":"{$tag.id}"{rdelim}{if !$smarty.foreach.tags.last},{/if}{/foreach}],
+ {ldelim}
+ hintText: '{'Type in a search term'|@translate}',
+ noResultsText: '{'No results'|@translate}',
+ searchingText: '{'Searching...'|@translate}',
+ newText: ' ({'new'|@translate})',
+ animateDropdown: false,
+ preventDuplicates: true,
+ allowCreation: true
+ }
+ );
});
-{/literal}{/footer_script}
+{/footer_script}
{footer_script}
pwg_initialization_datepicker("#date_creation_day", "#date_creation_month", "#date_creation_year", "#date_creation_linked_date", "#date_creation_action_set");
@@ -136,8 +138,8 @@ pwg_initialization_datepicker("#date_creation_day", "#date_creation_month", "#da
<td><strong>{'Tags'|@translate}</strong></td>
<td>
<select id="tags" name="tags">
-{foreach from=$tags item=tag}
- <option value="{$tag.value}" class="selected">{$tag.key}</option>
+{foreach from=$tag_selection item=tag}
+ <option value="{$tag.id}" class="selected">{$tag.name}</option>
{/foreach}
</select>
</td>
diff --git a/admin/themes/default/theme.css b/admin/themes/default/theme.css
index e3cab2eff..870844682 100644
--- a/admin/themes/default/theme.css
+++ b/admin/themes/default/theme.css
@@ -585,50 +585,6 @@ img.ui-datepicker-trigger {
margin:-3px 5px 2px 5px;
}
-/* jQuery FCBKcomplete */
-/* TextboxList sample CSS */
-ul.holder { margin: 0; border: 1px solid #999; overflow: hidden; height: auto !important; height: 1%; padding: 4px 5px 0; }
-*:first-child+html ul.holder { padding-bottom: 2px; } * html ul.holder { padding-bottom: 2px; } /* ie7 and below */
-ul.holder li { float: left; list-style-type: none; margin: 0 5px 4px 0; white-space:nowrap;}
-ul.holder li.bit-box, ul.holder li.bit-input input { font: 11px "Lucida Grande", "Verdana"; }
-ul.holder li.bit-box { -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; border: 1px solid #CAD8F3; background: #DEE7F8; padding: 1px 5px 2px; }
-ul.holder li.bit-box-focus { border-color: #598BEC; background: #598BEC; color: #fff; }
-ul.holder li.bit-input input { width: auto; overflow:visible; margin: 0; border: 0px; outline: 0; padding: 3px 0px 2px; } /* no left/right padding here please */
-ul.holder li.bit-input input.smallinput { width: 20px; }
-
-/* Facebook demo CSS */
-#add { border: 1px solid #999; width: 550px; margin: 50px; padding: 20px 30px 10px; }
-form ol li { list-style-type: none; }
-form ol { font: 11px "Lucida Grande", "Verdana"; margin: 0; padding: 0; }
-form ol li.input-text { margin-bottom: 10px; list-style-type: none; padding-bottom: 10px; }
-form ol li.input-text label { font-weight: bold; cursor: pointer; display: block; font-size: 13px; margin-bottom: 10px; }
-form ol li.input-text input { width: 500px; padding: 5px 5px 6px; font: 11px "Lucida Grande", "Verdana"; border: 1px solid #999; }
-form ul.holder { width: 500px; }
-form ul { margin: 0 !important }
-ul.holder li.bit-box, #apple-list ul.holder li.bit-box { padding-right: 15px; position: relative; z-index:1000;}
-#apple-list ul.holder li.bit-input { margin: 0; }
-#apple-list ul.holder li.bit-input input.smallinput { width: 5px; }
-ul.holder li.bit-hover { background: #BBCEF1; border: 1px solid #6D95E0; }
-ul.holder li.bit-box-focus { border-color: #598BEC; background: #598BEC; color: #fff; }
-ul.holder li.bit-box a.closebutton { position: absolute; right: 4px; top: 5px; display: block; width: 7px; height: 7px; font-size: 1px; background: url(icon/fcbkcomplete_close.gif); }
-ul.holder li.bit-box a.closebutton:hover { background-position: 7px; }
-ul.holder li.bit-box-focus a.closebutton, ul.holder li.bit-box-focus a.closebutton:hover { background-position: bottom; }
-
-/* Autocompleter */
-
-.facebook-auto { display: none; position: absolute; width: 512px; background: #eee; }
-.facebook-auto .default { padding: 5px 7px; border: 1px solid #ccc; border-width: 0 1px 1px;font-family:"Lucida Grande","Verdana"; font-size:11px; }
-.facebook-auto ul { display: none; margin: 0; padding: 0; overflow: auto; position:absolute; z-index:9999}
-.facebook-auto ul li { padding: 5px 12px; z-index: 1000; cursor: pointer; margin: 0; list-style-type: none; border: 1px solid #ccc; border-width: 0 1px 1px; font: 11px "Lucida Grande", "Verdana"; background-color: #eee }
-.facebook-auto ul li em { font-weight: bold; font-style: normal; background: #ccc; }
-.facebook-auto ul li.auto-focus { background: #4173CC; color: #fff; }
-.facebook-auto ul li.auto-focus em { background: none; }
-.deleted { background-color:#4173CC !important; color:#ffffff !important;}
-.hidden { display:none;}
-
-#demo ul.holder li.bit-input input { padding: 2px 0 1px; border: 1px solid #999; }
-.ie6fix {height:1px;width:1px; position:absolute;top:0px;left:0px;z-index:1;}
-
/* Add photos, direct mode */
#uploadBoxes P {
margin:0;
@@ -1045,6 +1001,24 @@ LEGEND {
#batchManagerGlobal #applyFilterBlock {margin-top:20px;}
#batchManagerGlobal .useFilterCheckbox {display:none}
+
+/* TokenInput (with Facebook style) */
+ul.token-input-list {overflow: hidden; height: auto !important; height: 1%;width: 400px;border: 1px solid #8496ba;cursor: text;font-size: 12px;font-family: Verdana;min-height: 1px;z-index: 999;margin: 0;padding: 0;background-color: #fff;list-style-type: none;clear: left;}
+ul.token-input-list li input {border: 0;width: 100px;padding: 3px 8px;background-color: white;margin: 2px 0;-webkit-appearance: caret;}
+li.token-input-token {overflow: hidden; height: auto !important; height: 15px;margin: 3px;padding: 1px 3px;background-color: #eff2f7;color: #000;cursor: default;border: 1px solid #ccd5e4;font-size: 11px;border-radius: 5px;-moz-border-radius: 5px;-webkit-border-radius: 5px;float: left;white-space: nowrap;}
+li.token-input-token p {display: inline;padding: 0;margin: 0;}
+li.token-input-token span {color: #a6b3cf;margin-left: 5px;font-weight: bold;cursor: pointer;}
+li.token-input-selected-token {background-color: #5670a6;border: 1px solid #3b5998;color: #fff;}
+li.token-input-input-token {float: left;margin: 0;padding: 0;list-style-type: none;width:10px;}
+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: 11px;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;margin: 0;list-style-type: none;}
+div.token-input-dropdown ul li.token-input-dropdown-item {background-color: #fff;}
+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: #3b5998;color: #fff;}
+
.warning {
background:url(icon/warning.png) no-repeat top left;
width: 130px;
diff --git a/admin/themes/roma/theme.css b/admin/themes/roma/theme.css
index 8830f5a12..3fb62a2df 100644
--- a/admin/themes/roma/theme.css
+++ b/admin/themes/roma/theme.css
@@ -245,3 +245,10 @@ display:block; height:85px; left:225px; position:relative; top:-42px; width:313p
#batchManagerGlobal .thumbSelected {background-color:#555 !important}
#batchManagerGlobal #selectedMessage {background-color:#555; color:#ddd;}
+/* TokenInput (with Facebook style for ROMA) */
+ul.token-input-list {border-color:#666;background-color:#444;}
+ul.token-input-list li input {background-color:#444;}
+li.token-input-token span {color:#878787;}
+div.token-input-dropdown {background-color:#eee;border-color:#666;}
+div.token-input-dropdown ul li {background-color:#eee;}
+div.token-input-dropdown ul li.token-input-selected-dropdown-item {background-color:#FF7800;}
diff --git a/language/en_UK/admin.lang.php b/language/en_UK/admin.lang.php
index cf1bc978a..0149292fb 100644
--- a/language/en_UK/admin.lang.php
+++ b/language/en_UK/admin.lang.php
@@ -429,6 +429,7 @@ $lang['Month'] = "Month";
$lang['Move albums'] = "Move albums";
$lang['Move'] = "Move";
$lang['Name'] = "Name";
+$lang['new'] = "new";
$lang['New name'] = "New name";
$lang['New parent album'] = "New parent album";
$lang['New photos added'] = "New photos added";
@@ -443,6 +444,7 @@ $lang['No photo in the current set.'] = 'No photo in the current set.';
$lang['No photo in this album'] = "No photo in this album";
$lang['No photo selected, %d photos in current set'] = 'No photo selected, %d photos in current set';
$lang['No photo selected, no action possible.'] = 'No photo selected, no action possible.';
+$lang['No results'] = "No results";
$lang['No user to send notifications by mail.'] = "No user to be notified by mail.";
$lang['no write access'] = "no write access";
$lang['none'] = "none";
@@ -576,6 +578,7 @@ $lang['Save page visits by guests'] = "Record pages visited by guests";
$lang['Save page visits by users'] = "Record pages visited by users";
$lang['Save Settings'] = 'Save Settings';
$lang['Save to permalink history'] = "Save to permalinks history";
+$lang['Searching...'] = "Searching...";
$lang['Search for new images in the directories'] = "Search for new images in the directories";
$lang['Section'] = "Section";
$lang['See you soon,'] = "See you soon,";
@@ -691,6 +694,7 @@ $lang['Tools'] = "Tools";
$lang['total time'] = "total time";
$lang['Type here the author name'] = 'Type here the author name';
$lang['Type here the title'] = 'Type here the title';
+$lang['Type in a search term'] = "Type in a search term";
$lang['Unable to check for upgrade.'] = "Unable to check for upgrade.";
$lang['Uncheck all'] = "Uncheck all";
$lang['Uninstall'] = "Uninstall";
diff --git a/language/fr_FR/admin.lang.php b/language/fr_FR/admin.lang.php
index 466ff60a1..6a2882a1a 100644
--- a/language/fr_FR/admin.lang.php
+++ b/language/fr_FR/admin.lang.php
@@ -794,4 +794,8 @@ $lang['Do you want to activate anyway?'] = 'Voulez-vous l\'activer quand même?'
$lang['THIS PLUGIN IS NOW PART OF PIWIGO CORE! DELETE IT NOW.'] = 'CE PLUGIN FAIT DÉSORMAIS PARTIE DU CORE DE PIWIGO! SUPPRIMEZ-LE.';
$lang['ERROR: THIS PLUGIN IS MISSING BUT IT IS INSTALLED! UNINSTALL IT NOW.'] = 'ERREUR: CE PLUGIN EST MANQUANT MAIS TOUJOURS INSTALLÉ! DÉSINSTALLEZ-LE.';
$lang['display'] = 'Afficher';
+$lang['No results'] = "Pas de résultat";
+$lang['Type in a search term'] = "Entrez un terme de recherche";
+$lang['Searching...'] = "Recherche...";
+$lang['new'] = "nouveau";
?>
diff --git a/themes/default/js/plugins/jquery.fcbkcomplete.js b/themes/default/js/plugins/jquery.fcbkcomplete.js
deleted file mode 100644
index 3fb094305..000000000
--- a/themes/default/js/plugins/jquery.fcbkcomplete.js
+++ /dev/null
@@ -1,645 +0,0 @@
-/**
- FCBKcomplete 2.7.5
- - Jquery version required: 1.2.x, 1.3.x, 1.4.x
-
- Based on TextboxList by Guillermo Rauch http://devthought.com/
-
- Changelog:
- - 2.00 new version of fcbkcomplete
-
- - 2.01 fixed bugs & added features
- fixed filter bug for preadded items
- focus on the input after selecting tag
- the element removed pressing backspace when the element is selected
- input tag in the control has a border in IE7
- added iterate over each match and apply the plugin separately
- set focus on the input after selecting tag
-
- - 2.02 fixed fist element selected bug
- fixed defaultfilter error bug
-
- - 2.5 removed selected="selected" attribute due ie bug
- element search algorithm changed
- better performance fix added
- fixed many small bugs
- onselect event added
- onremove event added
-
- - 2.6 ie6/7 support fix added
- added new public method addItem due request
- added new options "firstselected" that you can set true/false to select first element on dropdown list
- autoexpand input element added
- removeItem bug fixed
- and many more bug fixed
- fixed public method to use it $("elem").trigger("addItem",[{"title": "test", "value": "test"}]);
-
-- 2.7 jquery 1.4 compability
- item lock possability added by adding locked class to preadded option <option value="value" class="selected locked">text</option>
- maximum item that can be added
-
-- 2.7.1 bug fixed
- ajax delay added thanks to http://github.com/dolorian
-
-- 2.7.2 some minor bug fixed
- minified version recompacted due some problems
-
-- 2.7.3 event call fixed thanks to William Parry <williamparry!at!gmail.com>
-
-- 2.7.4 standart event change call added on addItem, removeItem
- preSet also check if item have "selected" attribute
- addItem minor fix
-
-- 2.7.5 event call removeItem fixed
- new public method destroy added needed to remove fcbkcomplete element from dome
-
- */
-/* Coded by: emposha <admin@emposha.com> */
-/* Copyright: Emposha.com <http://www.emposha.com> - Distributed under MIT - Keep this message! */
-
-/**
- * json_url - url to fetch json object
- * cache - use cache
- * height - maximum number of element shown before scroll will apear
- * newel - show typed text like a element
- * firstselected - automaticly select first element from dropdown
- * filter_case - case sensitive filter
- * filter_selected - filter selected items from list
- * complete_text - text for complete page
- * maxshownitems - maximum numbers that will be shown at dropdown list (less better performance)
- * onselect - fire event on item select
- * onremove - fire event on item remove
- * maxitimes - maximum items that can be added
- * delay - delay between ajax request (bigger delay, lower server time request)
- * addontab - add first visible element on tab or enter hit
- * attachto - after this element fcbkcomplete insert own elements
- */
-jQuery(function($) {
- $.fn.fcbkcomplete = function(opt) {
- return this.each(function() {
- function init() {
- createFCBK();
- preSet();
- addInput(0);
- }
-
- function createFCBK() {
- element.hide();
- element.attr("multiple", "multiple");
- if (element.attr("name").indexOf("[]") == -1) {
- element.attr("name", element.attr("name") + "[]");
- }
-
- holder = $(document.createElement("ul"));
- holder.attr("class", "holder");
-
- if (options.attachto) {
- if (typeof(options.attachto) == "object") {
- options.attachto.append(holder);
- }
- else {
- $(options.attachto).append(holder);
- }
-
- }
- else {
- element.after(holder);
- }
-
- complete = $(document.createElement("div"));
- complete.addClass("facebook-auto");
- complete.append('<div class="default">' + options.complete_text + "</div>");
- complete.hover(function() {options.complete_hover = 0;}, function() {options.complete_hover = 1;});
-
- feed = $(document.createElement("ul"));
- feed.attr("id", elemid + "_feed");
-
- complete.prepend(feed);
- holder.after(complete);
- feed.css("width", complete.width());
- }
-
- function preSet() {
- element.children("option").each(function(i, option) {
- option = $(option);
- if (option.hasClass("selected")) {
- addItem(option.text(), option.val(), true, option.hasClass("locked"));
- option.attr("selected", "selected");
- }
- cache.push({
- key: option.text(),
- value: option.val()
- });
- search_string += "" + (cache.length - 1) + ":" + option.text() + ";";
- });
- }
-
- //public method to add new item
- $(this).bind("addItem",
- function(event, data) {
- addItem(data.title, data.value, 0, 0, 0);
- });
-
- //public method to remove item
- $(this).bind("removeItem",
- function(event, data) {
- var item = holder.children('li[rel=' + data.value + ']');
- if (item.length) {
- removeItem(item);
- }
- });
-
- //public method to remove item
- $(this).bind("destroy",
- function(event, data) {
- holder.remove();
- complete.remove();
- element.show();
- });
-
- function addItem(title, value, preadded, locked, focusme) {
- if (!maxItems()) {
- return false;
- }
- var li = document.createElement("li");
- var txt = document.createTextNode(title);
- var aclose = document.createElement("a");
- var liclass = "bit-box" + (locked ? " locked": "");
- $(li).attr({
- "class": liclass,
- "rel": value
- });
- $(li).prepend(txt);
- $(aclose).attr({
- "class": "closebutton",
- "href": "#"
- });
-
- li.appendChild(aclose);
- holder.append(li);
-
- $(aclose).click(function() {
- removeItem($(this).parent("li"));
- return false;
- });
-
- if (!preadded) {
- $("#" + elemid + "_annoninput").remove();
- var _item;
- addInput(focusme);
- if (element.children("option[value=" + value + "]").length) {
- _item = element.children("option[value=" + value + "]");
- _item.get(0).setAttribute("selected", "selected");
- _item.attr("selected", "selected");
- if (!_item.hasClass("selected")) {
- _item.addClass("selected");
- }
- }
- else{
- var _item = $(document.createElement("option"));
- _item.attr("value", value).get(0).setAttribute("selected", "selected");
- _item.attr("value", value).attr("selected", "selected");
- _item.attr("value", value).addClass("selected");
- _item.text(title);
- element.append(_item);
- }
- if (options.onselect) {
- funCall(options.onselect, _item)
- }
- element.change();
- }
- holder.children("li.bit-box.deleted").removeClass("deleted");
- feed.hide();
- }
-
- function removeItem(item) {
-
- if (!item.hasClass('locked')) {
- item.fadeOut("fast");
- if (options.onremove) {
- var _item = element.children("option[value=" + item.attr("rel") + "]");
- funCall(options.onremove, _item)
- }
- element.children('option[value="' + item.attr("rel") + '"]').removeAttr("selected").removeClass("selected");
- item.remove();
- element.change();
- deleting = 0;
- }
- }
-
- function addInput(focusme) {
- var li = $(document.createElement("li"));
- var input = $(document.createElement("input"));
- var getBoxTimeout = 0;
-
- li.attr({
- "class": "bit-input",
- "id": elemid + "_annoninput"
- });
- input.attr({
- "type": "text",
- "class": "maininput",
- "size": "1"
- });
- holder.append(li.append(input));
-
- input.focus(function() {
- complete.fadeIn("fast");
- });
-
- input.blur(function() {
- if (options.complete_hover) {
- complete.fadeOut("fast");
- }
- else {
- input.focus();
- }
- });
-
- holder.click(function() {
- input.focus();
- if (feed.length && input.val().length) {
- feed.show();
- }
- else{
- feed.hide();
- complete.children(".default").show();
- }
- });
-
- input.keypress(function(event) {
- if (event.keyCode == 13) {
- return false;
- }
- //auto expand input
- input.attr("size", input.val().length + 1);
- });
-
- input.keydown(function(event) {
- //prevent to enter some bad chars when input is empty
- if (event.keyCode == 191) {
- event.preventDefault();
- return false;
- }
- });
-
- input.keyup(function(event) {
- var etext = xssPrevent(input.val());
-
- if (event.keyCode == 8 && etext.length == 0) {
- feed.hide();
- if (!holder.children("li.bit-box:last").hasClass('locked')) {
- if (holder.children("li.bit-box.deleted").length == 0) {
- holder.children("li.bit-box:last").addClass("deleted");
- return false;
- }
- else{
- if (deleting) {
- return;
- }
- deleting = 1;
- holder.children("li.bit-box.deleted").fadeOut("fast",
- function() {
- removeItem($(this));
- return false;
- });
- }
- }
- }
-
- if (event.keyCode != 40 && event.keyCode != 38 && event.keyCode!=37 && event.keyCode!=39 && etext.length != 0) {
- counter = 0;
-
- if (options.json_url) {
- if (options.cache && json_cache) {
- addMembers(etext);
- bindEvents();
- }
- else{
- getBoxTimeout++;
- var getBoxTimeoutValue = getBoxTimeout;
- setTimeout(function() {
- if (getBoxTimeoutValue != getBoxTimeout) return;
- $.getJSON(options.json_url, {tag: etext},
- function(data) {
- addMembers(etext, data);
- json_cache = true;
- bindEvents();
- });
- },
- options.delay);
- }
- }
- else{
- addMembers(etext);
- bindEvents();
- }
- complete.children(".default").hide();
- feed.show();
- }
- });
- if (focusme) {
- setTimeout(function() {
- input.focus();
- complete.children(".default").show();
- },
- 1);
- }
- }
-
- function addMembers(etext, data) {
- feed.html('');
-
- if (!options.cache && data != null) {
- cache = new Array();
- search_string = "";
- }
-
- addTextItem(etext);
-
- if (data != null && data.length) {
- $.each(data,
- function(i, val) {
- cache.push({
- key: val.key,
- value: val.value
- });
- search_string += "" + (cache.length - 1) + ":" + val.key + ";";
- });
- }
-
- var maximum = options.maxshownitems < cache.length ? options.maxshownitems: cache.length;
- var filter = "i";
- if (options.filter_case) {
- filter = "";
- }
-
- var myregexp,
- match;
- try{
- myregexp = eval('/(?:^|;)\\s*(\\d+)\\s*:[^;]*?' + etext + '[^;]*/g' + filter);
- match = myregexp.exec(search_string);
- }
- catch(ex) {
- };
-
- var content = '';
- while (match != null && maximum > 0) {
- var id = match[1];
- var object = cache[id];
- if (options.filter_selected && element.children("option[value=" + object.value + "]").hasClass("selected")) {
- //nothing here...
- }
- else{
- content += '<li rel="' + object.value + '">' + itemIllumination(object.key, etext) + '</li>';
- counter++;
- maximum--;
- }
- match = myregexp.exec(search_string);
- }
- feed.append(content);
-
- if (options.firstselected) {
- focuson = feed.children("li:visible:first");
- focuson.addClass("auto-focus");
- }
-
- if (counter > options.height) {
- feed.css({
- "height": (options.height * 24) + "px",
- "overflow": "auto"
- });
- }
- else{
- feed.css("height", "auto");
- }
- }
-
- function itemIllumination(text, etext) {
- if (options.filter_case) {
- try{
- eval("var text = text.replace(/(.*)(" + etext + ")(.*)/gi,'$1<em>$2</em>$3');");
- }
- catch(ex) {
- };
- }
- else{
- try{
- eval("var text = text.replace(/(.*)(" + etext.toLowerCase() + ")(.*)/gi,'$1<em>$2</em>$3');");
- }
- catch(ex) {
- };
- }
- return text;
- }
-
- function bindFeedEvent() {
- feed.children("li").mouseover(function() {
- feed.children("li").removeClass("auto-focus");
- $(this).addClass("auto-focus");
- focuson = $(this);
- });
-
- feed.children("li").mouseout(function() {
- $(this).removeClass("auto-focus");
- focuson = null;
- });
- }
-
- function removeFeedEvent() {
- feed.children("li").unbind("mouseover");
- feed.children("li").unbind("mouseout");
- feed.mousemove(function() {
- bindFeedEvent();
- feed.unbind("mousemove");
- });
- }
-
- function bindEvents() {
- var maininput = $("#" + elemid + "_annoninput").children(".maininput");
- bindFeedEvent();
- feed.children("li").unbind("mousedown");
- feed.children("li").mousedown(function() {
- var option = $(this);
- addItem(option.text(), option.attr("rel"), 0, 0, 1);
- feed.hide();
- complete.hide();
- });
-
- maininput.unbind("keydown");
- maininput.keydown(function(event) {
- if (event.keyCode == 191) {
- event.preventDefault();
- return false;
- }
-
- if (event.keyCode != 8) {
- holder.children("li.bit-box.deleted").removeClass("deleted");
- }
-
- if ((event.keyCode == 13 || event.keyCode == 9) && checkFocusOn()) {
- var option = focuson;
- addItem(option.text(), option.attr("rel"), 0, 0, 1);
- complete.hide();
- event.preventDefault();
- focuson = null;
- return false;
- }
-
- if ((event.keyCode == 13 || event.keyCode == 9) && !checkFocusOn()) {
- if (options.newel) {
- var value = xssPrevent($(this).val());
- addItem(value, value, 0, 0, 1);
- complete.hide();
- event.preventDefault();
- focuson = null;
- return false;
- }
-
- if (options.addontab) {
- focuson = feed.children("li:visible:first");
- var option = focuson;
- addItem(option.text(), option.attr("rel"), 0, 0, 1);
- complete.hide();
- event.preventDefault();
- focuson = null;
- return false;
- }
- }
-
- if (event.keyCode == 40) {
- removeFeedEvent();
- if (focuson == null || focuson.length == 0) {
- focuson = feed.children("li:visible:first");
- feed.get(0).scrollTop = 0;
- }
- else{
- focuson.removeClass("auto-focus");
- focuson = focuson.nextAll("li:visible:first");
- var prev = parseInt(focuson.prevAll("li:visible").length, 10);
- var next = parseInt(focuson.nextAll("li:visible").length, 10);
- if ((prev > Math.round(options.height / 2) || next <= Math.round(options.height / 2)) && typeof(focuson.get(0)) != "undefined") {
- feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight, 10) * (prev - Math.round(options.height / 2));
- }
- }
- feed.children("li").removeClass("auto-focus");
- focuson.addClass("auto-focus");
- }
- if (event.keyCode == 38) {
- removeFeedEvent();
- if (focuson == null || focuson.length == 0) {
- focuson = feed.children("li:visible:last");
- feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight, 10) * (parseInt(feed.children("li:visible").length, 10) - Math.round(options.height / 2));
- }
- else{
- focuson.removeClass("auto-focus");
- focuson = focuson.prevAll("li:visible:first");
- var prev = parseInt(focuson.prevAll("li:visible").length, 10);
- var next = parseInt(focuson.nextAll("li:visible").length, 10);
- if ((next > Math.round(options.height / 2) || prev <= Math.round(options.height / 2)) && typeof(focuson.get(0)) != "undefined") {
- feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight, 10) * (prev - Math.round(options.height / 2));
- }
- }
- feed.children("li").removeClass("auto-focus");
- focuson.addClass("auto-focus");
- }
- });
- }
-
- function maxItems() {
- if (options.maxitems != 0) {
- if (holder.children("li.bit-box").length < options.maxitems) {
- return true;
- }
- else{
- return false;
- }
- }
- }
-
- function addTextItem(value) {
- if (options.newel && maxItems()) {
- feed.children("li[fckb=1]").remove();
- if (value.length == 0) {
- return;
- }
- var li = $(document.createElement("li"));
- li.attr({
- "rel": value,
- "fckb": "1"
- }).html(value);
- feed.prepend(li);
- counter++;
- }
- else{
- return;
- }
- }
-
- function funCall(func, item) {
- var _object = "";
- for (i = 0; i < item.get(0).attributes.length; i++) {
- if (item.get(0).attributes[i].nodeValue != null) {
- _object += "\"_" + item.get(0).attributes[i].nodeName + "\": \"" + item.get(0).attributes[i].nodeValue + "\",";
- }
- }
- _object = "{" + _object + " notinuse: 0}";
- func.call(func, _object);
- }
-
- function checkFocusOn() {
- if (focuson == null) {
- return false;
- }
- if (focuson.length == 0) {
- return false;
- }
- return true;
- }
-
- function xssPrevent(string) {
- string = string.replace(/[\"\'][\s]*javascript:(.*)[\"\']/g, "\"\"");
- string = string.replace(/script(.*)/g, "");
- string = string.replace(/eval\((.*)\)/g, "");
- string = string.replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '');
- return string;
- }
-
- var options = $.extend({
- json_url: null,
- cache: false,
- height: "10",
- newel: false,
- addontab: false,
- firstselected: false,
- filter_case: false,
- filter_selected: false,
- complete_text: "Start to type...",
- maxshownitems: 30,
- maxitems: 10,
- onselect: null,
- onremove: null,
- attachto: null,
- delay: 350
- },
- opt);
-
- //system variables
- var holder = null;
- var feed = null;
- var complete = null;
- var counter = 0;
- var cache = new Array();
- var json_cache = false;
- var search_string = "";
- var focuson = null;
- var deleting = 0;
- var complete_hover = 1;
-
- var element = $(this);
- var elemid = element.attr("id");
- init();
-
- return this;
- });
- };
-});
diff --git a/themes/default/js/plugins/jquery.tokeninput.js b/themes/default/js/plugins/jquery.tokeninput.js
new file mode 100644
index 000000000..0c8662b3b
--- /dev/null
+++ b/themes/default/js/plugins/jquery.tokeninput.js
@@ -0,0 +1,777 @@
+/**
+ DON'T MAKE AUTOMATIC UPGRADE
+ This is a merged version of :
+ + https://github.com/gr2m/jquery-tokeninput/
+ + https://github.com/mistic100/jquery-tokeninput/
+*/
+
+/*
+ * jQuery Plugin: Tokenizing Autocomplete Text Entry
+ * Version 1.4.2
+ *
+ * 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 = {
+ hintText: "Type in a search term",
+ noResultsText: "No results",
+ searchingText: "Searching...",
+ newText: "(new)",
+ deleteText: "&times;",
+ searchDelay: 300,
+ minChars: 1,
+ tokenLimit: null,
+ jsonContainer: null,
+ method: "GET",
+ contentType: "json",
+ queryParam: "q",
+ tokenDelimiter: ",",
+ preventDuplicates: false,
+ prePopulate: null,
+ processPrePopulate: false,
+ animateDropdown: true,
+ onResult: null,
+ onAdd: null,
+ onDelete: null,
+ allowCreation: false
+};
+
+// 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
+};
+
+
+// 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);
+ });
+};
+
+
+// TokenList class for each input
+$.TokenList = function (input, url_or_data, settings) {
+ //
+ // Initialization
+ //
+
+ // Configure the data source
+ if(typeof(url_or_data) === "string") {
+ // Set the url to query against
+ settings.url = url_or_data;
+
+ // Make a smart guess about cross-domain if it wasn't explicitly specified
+ if(settings.crossDomain === undefined) {
+ if(settings.url.indexOf("://") === -1) {
+ settings.crossDomain = false;
+ } else {
+ settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.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"
+ })
+ .focus(function () {
+ if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
+ show_dropdown_hint();
+ }
+ })
+ .blur(function () {
+ hide_dropdown();
+ })
+ .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));
+ } 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));
+ 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;
+ }
+ });
+
+ if ($(input).get(0).tagName == 'SELECT') {
+ // Create a new input to store selected tokens, original will be delete later
+ var hidden_input = $("<input type=\"text\" name=\"" + $(input).attr('name') + "\" autocomplete=\"off\">")
+ .hide()
+ .val("")
+ .focus(function () {
+ input_box.focus();
+ })
+ .blur(function () {
+ input_box.blur();
+ })
+ .insertBefore(input);
+ } else {
+ // 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.id, value.name);
+ });
+ }
+
+ // Pre-populate from SELECT options
+ if ($(input).get(0).tagName == 'SELECT') {
+ $(input).children('option').each(function () {
+ insert_token($(this).attr('value'), $(this).text());
+ });
+ }
+
+
+
+ //
+ // Private functions
+ //
+
+ 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(id, value) {
+ var this_token = $("<li><p>"+ value +"</p></li>")
+ .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());
+ return false;
+ });
+
+ // Store data on the token
+ var token_data = {"id": id, "name": value};
+ $.data(this_token.get(0), "tokeninput", token_data);
+
+ // 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
+ var token_ids = $.map(saved_tokens, function (el) {
+ return el.id;
+ });
+ hidden_input.val(token_ids.join(settings.tokenDelimiter));
+
+ token_count += 1;
+
+ 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
+ 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 === li_data.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
+ 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();
+ }
+
+ // 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,li_data);
+ }
+ }
+
+ // 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
+ var token_ids = $.map(saved_tokens, function (el) {
+ return el.id;
+ });
+ hidden_input.val(token_ids.join(settings.tokenDelimiter));
+
+ 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);
+ }
+ }
+
+ // 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>");
+ }
+
+ // 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"));
+ return false;
+ })
+ .hide();
+
+ $.each(results, function(index, value) {
+ var this_li = $("<li>" + highlight_term(value.name, query) + "</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", {"id": value.id, "name": value.name});
+ });
+
+ 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 cached_results = cache.get(query);
+ if(cached_results) {
+ populate_dropdown(query, cached_results);
+ } else {
+ // Are we doing an ajax search or local data search?
+ if(settings.url) {
+ // Extract exisiting get params
+ var ajax_params = {};
+ ajax_params.data = {};
+ if(settings.url.indexOf("?") > -1) {
+ var parts = settings.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 = settings.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);
+ }
+
+ if(settings.allowCreation) {
+ results.push({name: input_box.val() + settings.newText, id: input_box.val()});
+ }
+ cache.add(query, 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.name.toLowerCase().indexOf(query.toLowerCase()) > -1;
+ });
+
+ if($.isFunction(settings.onResult)) {
+ results = settings.onResult.call(hidden_input, results);
+ }
+
+ if(settings.allowCreation) {
+ results.push({name: input_box.val() + settings.newText, id: input_box.val()});
+ }
+
+ cache.add(query, results);
+
+ populate_dropdown(query, results);
+ }
+ }
+ }
+
+ if ($(input).get(0).tagName == 'SELECT') {
+ $(input).remove();
+ }
+};
+
+// 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));