Instantly share code, notes, and snippets.
Created
January 6, 2012 15:58
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save emodric/1571177 to your computer and use it in GitHub Desktop.
eZ Tags hack to support automatic placement of tags in content edit interface
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ezcss_require( array( 'tagssuggest.css', 'jqmodal.css', 'contentstructure-tree.css' ) )} | |
{ezscript_require( array( 'ezjsc::jquery', 'ezjsc::jqueryio', 'jqModal.js', 'jquery.tagsSuggest.js', 'tagsSuggest-init.js' ) )} | |
{def $has_add_access = false()} | |
{def $root_tag = fetch( tags, tag, hash( tag_id, $attribute.contentclass_attribute.data_int1 ) )} | |
{if $root_tag}{def $root_tag_id = $root_tag.id}{else}{def $root_tag_id = 0}{/if} | |
{def $user_limitations = user_limitations( 'tags', 'add' )} | |
{if $user_limitations['accessWord']|ne( 'no' )} | |
{if is_unset( $user_limitations['simplifiedLimitations']['Tag'] )} | |
{set $has_add_access = true()} | |
{elseif $root_tag} | |
{foreach $user_limitations['simplifiedLimitations']['Tag'] as $key => $value} | |
{if $root_tag.path_string|contains( concat( '/', $value, '/' ) )} | |
{set $has_add_access = true()} | |
{break} | |
{/if} | |
{/foreach} | |
{else} | |
{set $has_add_access = true()} | |
{set $root_tag = array()} | |
{foreach $user_limitations['simplifiedLimitations']['Tag'] as $key => $value} | |
{set $root_tag = $root_tag|append( fetch( tags, tag, hash( tag_id, $value ) ) )} | |
{/foreach} | |
{/if} | |
{/if} | |
{default attribute_base=ContentObjectAttribute} | |
<div class="tagssuggest{if $attribute.contentclass_attribute.data_int2} tagsfilter{/if}"> | |
<label>{'Selected tags'|i18n( 'extension/eztags/datatypes' )}:</label> | |
<div class="tags-list tags-listed no-results"> | |
<p class="loading">{'Loading'|i18n( 'extension/eztags/datatypes' )}...</p> | |
<p class="no-results">{'There are no selected tags'|i18n( 'extension/eztags/datatypes' )}.</p> | |
</div> | |
<label>{'Suggested tags'|i18n( 'extension/eztags/datatypes' )}:</label> | |
<div class="tags-list tags-suggested no-results"> | |
<p class="loading">{'Loading'|i18n( 'extension/eztags/datatypes' )}...</p> | |
<p class="no-results">{'There are no tags to suggest'|i18n( 'extension/eztags/datatypes' )}.</p> | |
</div> | |
<div class="tagssuggestfieldwrap"> | |
<input class="tagssuggestfield" type="text" size="70" name="suggest_{$attribute_base}_eztags_data_text_{$attribute.id}" value="" autocomplete="off" /> | |
</div> | |
{if $has_add_access} | |
<input type="button" value="{'Add new'|i18n( 'extension/eztags/datatypes' )}" name="AddTagButton" class="button-add-tag button-disabled" disabled="disabled" /> | |
<input type="hidden" value="{$root_tag_id}" name="RootTagID" class="root-tag-id" /> | |
{/if} | |
<input id="ezcoa-{if ne( $attribute_base, 'ContentObjectAttribute' )}{$attribute_base}-{/if}{$attribute.contentclassattribute_id}_{$attribute.contentclass_attribute_identifier}" class="box ezcc-{$attribute.object.content_class.identifier} ezcca-{$attribute.object.content_class.identifier}_{$attribute.contentclass_attribute_identifier} tagnames" type="hidden" name="{$attribute_base}_eztags_data_text_{$attribute.id}" value="{$attribute.content.keyword_string|wash}" /> | |
<input id="ezcoa2-{if ne( $attribute_base, 'ContentObjectAttribute' )}{$attribute_base}-{/if}{$attribute.contentclassattribute_id}_{$attribute.contentclass_attribute_identifier}" class="box tagpids" type="hidden" name="{$attribute_base}_eztags_data_text2_{$attribute.id}" value="{$attribute.content.parent_string|wash}" /> | |
<input id="ezcoa3-{if ne( $attribute_base, 'ContentObjectAttribute' )}{$attribute_base}-{/if}{$attribute.contentclassattribute_id}_{$attribute.contentclass_attribute_identifier}" class="box tagids" type="hidden" name="{$attribute_base}_eztags_data_text3_{$attribute.id}" value="{$attribute.content.id_string|wash}" /> | |
<input type="hidden" class="eztags_subtree_limit" name="eztags_subtree_limit-{$attribute.id}" value="{$attribute.contentclass_attribute.data_int1}" /> | |
<input type="hidden" class="eztags_hide_root_tag" name="eztags_hide_root_tag-{$attribute.id}" value="{$attribute.contentclass_attribute.data_int3}" /> | |
<input type="hidden" class="eztags_max_tags" name="eztags_max_tags-{$attribute.id}" value="{if $attribute.contentclass_attribute.data_int4|gt( 0 )}{$attribute.contentclass_attribute.data_int4}{else}0{/if}" /> | |
</div> | |
{if $has_add_access} | |
{include uri='design:ezjsctemplate/modal_dialog.tpl' attribute_id=$attribute.id root_tag=$root_tag} | |
{/if} | |
{/default} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function($) | |
{ | |
$.fn.tagsSuggest = function(settings) | |
{ | |
var defaults = { | |
searchId: $(this).attr('id'), | |
minCharacters: 1, | |
maxResults: undefined, | |
maxHeight:350, | |
ajaxResults: false, | |
suggestTimeout: 500, | |
ezjscAutocomplete:'ezjsctagssuggest::autocomplete', | |
ezjscSuggest:'ezjsctagssuggest::suggest' | |
}; | |
settings = $.extend(defaults, settings); | |
var timeout = null; | |
return this.each(function() | |
{ | |
var | |
base = $(this), | |
obj = $(this).find('.tagssuggestfield'), | |
root_tag_id = $(this).find('.root-tag-id').val(), | |
isFilter = $(this).hasClass('tagsfilter'), | |
names = $(this).find('.tagnames'), | |
parent_ids = $(this).find('.tagpids'), | |
tids = $(this).find('.tagids'), | |
parentSelectorButton = $(this).find('input[type="button"]'), | |
subtree_limit = $(this).find('.eztags_subtree_limit').val(), | |
hide_root_tag = $(this).find('.eztags_hide_root_tag').val(), | |
max_tags = $(this).find('.eztags_max_tags').val(), | |
parentSelector = $(this).siblings('.parent-selector-tree:eq(0)'), | |
results = $('<div />'), | |
currentSelection, pageX, pageY; | |
parentSelectorButton.click(function() { | |
addTagToList({'tag_name': obj.val().replace(/^\s+|\s+$/g, ''), 'tag_parent_id': root_tag_id, 'tag_id': '0'}, tags_listed, removeTagFromList, '×'); | |
updateValues(); | |
clearTagSearchField(); | |
emptyResults(); | |
hideResults(); | |
if ( isFilter ) runAutocomplete(); | |
}); | |
$(this).find('div.tags-listed').append('<ul class="float-break" />'); | |
$(this).find('div.tags-suggested').append('<ul class="float-break" />'); | |
var tags_listed = $(this).find('div.tags-listed ul'); | |
var tags_suggested = $(this).find('div.tags-suggested ul'); | |
if (names.val() && parent_ids.val() && tids.val()) | |
{ | |
tags_listed.parent('div.tags-list').removeClass('no-results'); | |
var tag_names_array = names.val().split('|#'); | |
var tag_parent_ids_array = parent_ids.val().split('|#'); | |
var tag_ids_array = tids.val().split('|#'); | |
$.each(tag_names_array, function(index, value) { | |
addTagToList({'tag_name': value.replace(/^\s+|\s+$/g, ''), 'tag_parent_id': tag_parent_ids_array[index].replace(/^\s+|\s+$/g, ''), 'tag_id': tag_ids_array[index].replace(/^\s+|\s+$/g, '')}, tags_listed, removeTagFromList, '×'); | |
}); | |
} | |
runSuggest(); | |
if ( isFilter ) runAutocomplete(); | |
function showHideInputElements() | |
{ | |
if ( max_tags > 0 ) { | |
if ( tags_listed.find('li').length >= max_tags ) { | |
base.find('.button-add-tag').hide(); | |
base.find('.tags-suggested').hide(); | |
base.find('.tags-suggested').prev('label').hide(); | |
base.find('.tagssuggestfieldwrap').hide(); | |
} | |
else { | |
base.find('.button-add-tag').show(); | |
base.find('.tags-suggested').show(); | |
base.find('.tags-suggested').prev('label').show(); | |
base.find('.tagssuggestfieldwrap').show(); | |
} | |
} | |
} | |
function addTagToList( item, list, callback, icon ) | |
{ | |
var tag = $('<li' + (!icon ? ' title="Add this tag"' : '') + '>' + item.tag_name + (icon ? '<a href="#" title="Remove tag">' + icon + '</a>' : '') + '</li>').data('tag', {'tag_parent_id': item.tag_parent_id, 'tag_name': item.tag_name, 'tag_id': item.tag_id}); | |
if (icon) tag.find('a').click(function(e) {callback(tag); return false;}) | |
else tag.click(function(e) {callback(tag); return false;}); | |
list.append(tag); | |
list.parent('div.tags-list').removeClass('no-results'); | |
showHideInputElements(); | |
} | |
function removeTagFromList(tag) | |
{ | |
$(tag).remove(); | |
updateValues(); | |
showHideInputElements(); | |
} | |
function moveTag(tag) | |
{ | |
var tag_data = $(tag).data('tag'); | |
addTagToList({'tag_parent_id': tag_data.tag_parent_id, 'tag_name': tag_data.tag_name, 'tag_id': tag_data.tag_id}, tags_listed, removeTagFromList, '×'); | |
removeTagFromList(tag); | |
//updateValues(); | |
showHideInputElements(); | |
} | |
function updateValues() | |
{ | |
var tag_names = ''; | |
var tag_parent_ids = ''; | |
var tag_tag_ids = ''; | |
tags_listed.find('li').each(function(i) | |
{ | |
tag_names += (tag_names == '' ? '' : '|#') + $(this).data('tag').tag_name; | |
tag_parent_ids += (tag_parent_ids == '' ? '' : '|#') + $(this).data('tag').tag_parent_id; | |
tag_tag_ids += (tag_tag_ids == '' ? '' : '|#') + $(this).data('tag').tag_id; | |
}); | |
names.val(tag_names); | |
parent_ids.val(tag_parent_ids); | |
tids.val(tag_tag_ids); | |
if (!tag_names && !tag_parent_ids && !tag_tag_ids) tags_listed.parent('div.tags-list').addClass('no-results'); | |
runSuggest(); | |
} | |
function emptyResults() | |
{ | |
$(results).html(''); | |
} | |
function hideResults() | |
{ | |
if ( !isFilter ) $(results).hide(); | |
} | |
function openParentSelector() | |
{ | |
hideResults(); | |
parentSelector.jqmShow(); | |
} | |
function bindParentSelectorTreeEvents() | |
{ | |
$('#' + parentSelector.attr('id') + ' .contentstructure a:not([class^=openclose])').live('click', function(e) | |
{ | |
addTagToList({'tag_name': obj.val().replace(/^\s+|\s+$/g, ''), 'tag_parent_id': $(this).attr('rel'), 'tag_id': '0'}, tags_listed, removeTagFromList, '×'); | |
updateValues(); | |
clearTagSearchField(); | |
emptyResults(); | |
hideResults(); | |
if ( isFilter ) runAutocomplete(); | |
parentSelector.jqmHide(); | |
return false; | |
}); | |
} | |
function setParentSelectorButtonState() | |
{ | |
if (obj.val().replace(/^\s+|\s+$/g, '')) | |
{ | |
parentSelectorButton.removeClass('button-disabled').addClass('button').removeAttr('disabled'); | |
} | |
else | |
{ | |
parentSelectorButton.removeClass('button').addClass('button-disabled').attr('disabled', 'disabled'); | |
} | |
} | |
function clearTagSearchField() | |
{ | |
obj.val(''); | |
setParentSelectorButtonState(); | |
} | |
function selectResultItem( item ) | |
{ | |
obj.val( item.tag_name ); | |
emptyResults(); | |
hideResults(); | |
addTagToList( item, tags_listed, removeTagFromList, '×' ); | |
updateValues(); | |
clearTagSearchField(); | |
if ( isFilter ) runAutocomplete(); | |
} | |
function setHoverClass(el) | |
{ | |
$('div.resultItem', results).removeClass('hover'); | |
$(el).addClass('hover'); | |
currentSelection = el; | |
} | |
function buildAutocomplete(resultObjects) | |
{ | |
var bOddRow = true, i, iFound = 0; | |
emptyResults(); | |
hideResults(); | |
for (i = 0; i < resultObjects.length; i += 1) | |
{ | |
var item = $('<div />'); | |
$(item).append('<p class="text">' + (resultObjects[i].tag_parent_name ? '<span class="count">(' + resultObjects[i].tag_parent_name + ')</span>' : '') + resultObjects[i].tag_name + '</p>'); | |
$(item).addClass('resultItem'). | |
addClass((bOddRow) ? 'odd' : 'even'). | |
click(function(n) {return function() { | |
selectResultItem(resultObjects[n]); | |
obj.focus(); | |
obj.val(obj.val());//move cursor to the string end (everybody say hello to ie) | |
};}(i)). | |
mouseover(function(el) {return function() { | |
setHoverClass(el); | |
};}(item)); | |
$(results).append(item); | |
bOddRow = !bOddRow; | |
iFound += 1; | |
if ( typeof settings.maxResults === 'number' && iFound >= settings.maxResults ) break; | |
} | |
$(results).find('.resultItem').wrapAll('<div class="results-wrap"></div>'); | |
if ($('.results-wrap div', results).length > 0) | |
{ | |
currentSelection = undefined; | |
$(results).prepend('<iframe frameborder="0"></iframe>').show().css('height', 'auto'); | |
if ($('.results-wrap', results).height() > settings.maxHeight) | |
{ | |
$('.results-wrap', results).css({'height': settings.maxHeight + 'px'}); | |
} | |
} | |
} | |
function runSuggest() { | |
tags_suggested.empty(); | |
var tag_names = names.val(); | |
//var content_title = $('input[id$="title"]:first').val(); | |
if (tag_names) | |
{ | |
tags_suggested.parent('div.tags-list').removeClass('no-results').addClass('loading'); | |
$.ez(settings.ezjscSuggest, {'tags_string': tag_names, 'subtree_limit': subtree_limit, 'hide_root_tag': hide_root_tag}, function(data) | |
{ | |
if (!data.content.tags.length) | |
{ | |
tags_suggested.parent('div.tags-list').addClass('no-results').removeClass('loading'); | |
return true; | |
} | |
tags_suggested.parent('div.tags-list').removeClass('loading'); | |
for (i = 0; i < data.content.tags.length; i += 1) | |
{ | |
addTagToList(data.content.tags[i], tags_suggested, moveTag, false); | |
} | |
}); | |
} | |
else | |
{ | |
tags_suggested.parent('div.tags-list').addClass('no-results').removeClass('loading'); | |
} | |
} | |
function runAutocomplete() | |
{ | |
if ( obj.val() || isFilter ) | |
$.ez(settings.ezjscAutocomplete, {'search_string': obj.val(), 'subtree_limit': subtree_limit, 'hide_root_tag': hide_root_tag}, function(data) | |
{ | |
if (typeof data === 'string') data = JSON.parse(data); | |
buildAutocomplete(data.content.tags); | |
}); | |
else | |
{ | |
emptyResults(); | |
hideResults(); | |
} | |
} | |
function keyListener(e) { | |
switch (e.keyCode) { | |
case 9: // tab key | |
if (e.type == 'keydown') { | |
if ($(results).css('display') == 'block' && $(results).find('.resultItem.hover').length) { | |
e.preventDefault(); | |
$(currentSelection).trigger('click'); | |
} | |
} | |
return true; | |
case 13: // return key | |
if (e.type == 'keydown') { | |
e.preventDefault(); | |
if ($(results).css('display') == 'block' && $(results).find('.resultItem.hover').length) { | |
$(currentSelection).trigger('click'); | |
} | |
return true; | |
} | |
return false; | |
case 40: // down key | |
if (e.type == 'keydown') { | |
currentSelection = $(currentSelection).next().get(0); | |
if (typeof currentSelection === 'undefined') { | |
currentSelection = $('div.resultItem:first', results).get(0); | |
} | |
setHoverClass(currentSelection); | |
if (currentSelection) { | |
$('.results-wrap', results).scrollTop(currentSelection.offsetTop); | |
} | |
} | |
return false; | |
case 38: // up key | |
if (e.type == 'keydown') { | |
currentSelection = $(currentSelection).prev().get(0); | |
if (typeof currentSelection === 'undefined') { | |
currentSelection = $('div.resultItem:last', results).get(0); | |
} | |
setHoverClass(currentSelection); | |
if (currentSelection) { | |
$('.results-wrap', results).scrollTop(currentSelection.offsetTop); | |
} | |
} | |
return false; | |
default: | |
if (e.type == 'keyup') { | |
if (timeout) window.clearTimeout(timeout); | |
timeout = setTimeout(runAutocomplete, settings.suggestTimeout); | |
} | |
} | |
setParentSelectorButtonState(); | |
} | |
$(results).addClass('jsonSuggestResults'). | |
css({ | |
//'top': (obj.position().top + obj.height() + 5) + 'px', | |
'left': obj.position().left + 'px'//, | |
//'width': (obj.width() + 12) + 'px' | |
}).hide(); | |
obj.after(results); | |
//obj.after(parentSelector); | |
obj.keydown(keyListener).keyup(keyListener).blur(function(e) { | |
// We need to make sure we don't hide the result set | |
// if the input blur event is called because of clicking on | |
// a result item. | |
var resPos = $(results).offset(); | |
resPos.bottom = resPos.top + $(results).height(); | |
resPos.right = resPos.left + $(results).width(); | |
if (pageY < resPos.top || pageY > resPos.bottom || pageX < resPos.left || pageX > resPos.right) | |
{ | |
hideResults(); | |
} | |
}).focus(function(e) { | |
if ($('div', results).length > 0) { | |
$(results).show(); | |
} | |
}).attr('autocomplete', 'off'); | |
$('body').mousemove(function(e) {pageX = e.pageX; pageY = e.pageY;}); | |
// Opera doesn't seem to assign a keyCode for the down | |
// key on the keyup event. why? | |
if ($.browser.opera) { | |
obj.keydown(function(e) { | |
if (e.keyCode === 40) { // up key | |
return keyListener(e); | |
} | |
}); | |
} | |
}); | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment