2016-01-19 17:11:40 +00:00
/ * *
2016-02-01 17:22:26 +00:00
* @ brief Friendica people autocomplete
2016-01-19 17:11:40 +00:00
*
* require jQuery , jquery . textcomplete
2016-02-01 17:22:26 +00:00
*
* for further documentation look at :
* http : //yuku-t.com/jquery-textcomplete/
*
* https : //github.com/yuku-t/jquery-textcomplete/blob/master/doc/how_to_use.md
2016-01-19 17:11:40 +00:00
* /
2016-02-01 17:22:26 +00:00
2016-02-02 21:33:14 +00:00
function contact _search ( term , callback , backend _url , type , mode ) {
2016-01-19 17:11:40 +00:00
2016-01-21 12:28:29 +00:00
// Check if there is a conversation id to include the unkonwn contacts of the conversation
var conv _id = document . activeElement . id . match ( /\d+$/ ) ;
2016-01-19 17:11:40 +00:00
// Check if there is a cached result that contains the same information we would get with a full server-side search
var bt = backend _url + type ;
if ( ! ( bt in contact _search . cache ) ) contact _search . cache [ bt ] = { } ;
var lterm = term . toLowerCase ( ) ; // Ignore case
for ( var t in contact _search . cache [ bt ] ) {
if ( lterm . indexOf ( t ) >= 0 ) { // A more broad search has been performed already, so use those results
// Filter old results locally
var matching = contact _search . cache [ bt ] [ t ] . filter ( function ( x ) { return ( x . name . toLowerCase ( ) . indexOf ( lterm ) >= 0 || ( typeof x . nick !== 'undefined' && x . nick . toLowerCase ( ) . indexOf ( lterm ) >= 0 ) ) ; } ) ; // Need to check that nick exists because groups don't have one
2016-02-02 23:25:33 +00:00
matching . unshift ( { forum : false , text : term , replace : term } ) ;
2016-01-19 17:11:40 +00:00
setTimeout ( function ( ) { callback ( matching ) ; } , 1 ) ; // Use "pseudo-thread" to avoid some problems
return ;
}
}
var postdata = {
start : 0 ,
count : 100 ,
search : term ,
type : type ,
} ;
2016-01-21 12:28:29 +00:00
if ( conv _id !== null )
postdata [ 'conversation' ] = conv _id [ 0 ] ;
2016-02-02 21:33:14 +00:00
if ( mode !== null )
postdata [ 'mode' ] = mode ;
2016-01-19 17:11:40 +00:00
$ . ajax ( {
type : 'POST' ,
url : backend _url ,
data : postdata ,
dataType : 'json' ,
success : function ( data ) {
// Cache results if we got them all (more information would not improve results)
// data.count represents the maximum number of items
if ( data . items . length - 1 < data . count ) {
contact _search . cache [ bt ] [ lterm ] = data . items ;
}
var items = data . items . slice ( 0 ) ;
items . unshift ( { taggable : false , text : term , replace : term } ) ;
callback ( items ) ;
} ,
} ) . fail ( function ( ) { callback ( [ ] ) ; } ) ; // Callback must be invoked even if something went wrong.
}
contact _search . cache = { } ;
function contact _format ( item ) {
// Show contact information if not explicitly told to show something else
if ( typeof item . text === 'undefined' ) {
var desc = ( ( item . label ) ? item . nick + ' ' + item . label : item . nick ) ;
2016-02-02 23:25:33 +00:00
var forum = ( ( item . forum ) ? 'forum' : '' ) ;
2016-01-19 17:11:40 +00:00
if ( typeof desc === 'undefined' ) desc = '' ;
if ( desc ) desc = ' (' + desc + ')' ;
2016-02-02 23:25:33 +00:00
return "<div class='{0}' title='{4}'><img class='acpopup-img' src='{1}'><span class='acpopup-contactname'>{2}</span><span class='acpopup-sub-text'>{3}</span><div class='clear'></div></div>" . format ( forum , item . photo , item . name , desc , item . link ) ;
2016-01-19 17:11:40 +00:00
}
else
return "<div>" + item . text + "</div>" ;
}
function editor _replace ( item ) {
if ( typeof item . replace !== 'undefined' ) {
return '$1$2' + item . replace ;
}
// $2 ensures that prefix (@,@!) is preserved
var id = item . id ;
// 16 chars of hash should be enough. Full hash could be used if it can be done in a visually appealing way.
// 16 chars is also the minimum length in the backend (otherwise it's interpreted as a local id).
if ( id . length > 16 )
id = item . id . substring ( 0 , 16 ) ;
return '$1$2' + item . nick . replace ( ' ' , '' ) + '+' + id + ' ' ;
}
function basic _replace ( item ) {
if ( typeof item . replace !== 'undefined' )
return '$1' + item . replace ;
return '$1' + item . name + ' ' ;
}
function trim _replace ( item ) {
if ( typeof item . replace !== 'undefined' )
return '$1' + item . replace ;
return '$1' + item . name ;
}
function submit _form ( e ) {
$ ( e ) . parents ( 'form' ) . submit ( ) ;
}
/ * *
* jQuery plugin 'editor_autocomplete'
* /
( function ( $ ) {
$ . fn . editor _autocomplete = function ( backend _url ) {
2016-02-01 17:22:26 +00:00
// list of supported bbtags
var bbelements = [ 'b' , 'u' , 'i' , 'img' , 'url' , 'quote' , 'code' , 'spoiler' , 'audio' , 'video' , 'youtube' , 'map' , 'h1' , 'h2' , 'h3' , 'h4' , 'h5' , 'h6' , 's' , 'o' , 'list' , 'center' , 'nosmile' , 'vimeo' ] ;
2016-01-19 17:11:40 +00:00
// Autocomplete contacts
contacts = {
match : /(^|\s)(@\!*)([^ \n]+)$/ ,
index : 3 ,
search : function ( term , callback ) { contact _search ( term , callback , backend _url , 'c' ) ; } ,
replace : editor _replace ,
template : contact _format ,
} ;
2016-02-02 21:33:14 +00:00
// Autocomplete smilies e.g. ":like"
2016-01-19 17:11:40 +00:00
smilies = {
match : /(^|\s)(:[a-z]{2,})$/ ,
index : 2 ,
2016-02-01 17:22:26 +00:00
search : function ( term , callback ) { $ . getJSON ( 'smilies/json' ) . done ( function ( data ) { callback ( $ . map ( data , function ( entry ) { return entry . text . indexOf ( term ) === 0 ? entry : null ; } ) ) ; } ) ; } ,
template : function ( item ) { return item . icon + ' ' + item . text ; } ,
2016-01-19 17:11:40 +00:00
replace : function ( item ) { return "$1" + item . text + ' ' ; } ,
} ;
2016-02-01 17:22:26 +00:00
2016-02-02 21:33:14 +00:00
// Autocomplete BBTags
2016-02-01 17:22:26 +00:00
bbtags = {
match : /\[(\w*)$/ ,
index : 1 ,
search : function ( term , callback ) { callback ( $ . map ( bbelements , function ( element ) { return element . indexOf ( term ) === 0 ? element : null ; } ) ) ; } ,
replace : function ( element ) { return [ '[' + element + ']' , '[/' + element + ']' ] ; } ,
} ;
2016-01-19 17:11:40 +00:00
this . attr ( 'autocomplete' , 'off' ) ;
2016-02-01 17:22:26 +00:00
this . textcomplete ( [ contacts , smilies , bbtags ] , { className : 'acpopup' , zIndex : 1020 } ) ;
2016-01-19 17:11:40 +00:00
} ;
} ) ( jQuery ) ;
/ * *
* jQuery plugin 'search_autocomplete'
* /
( function ( $ ) {
$ . fn . search _autocomplete = function ( backend _url ) {
// Autocomplete contacts
contacts = {
match : /(^@)([^\n]{2,})$/ ,
index : 2 ,
2016-02-02 21:33:14 +00:00
search : function ( term , callback ) { contact _search ( term , callback , backend _url , 'x' , 'contact' ) ; } ,
replace : basic _replace ,
template : contact _format ,
} ;
// Autocomplete forum accounts
community = {
match : /(^!)([^\n]{2,})$/ ,
index : 2 ,
search : function ( term , callback ) { contact _search ( term , callback , backend _url , 'x' , 'community' ) ; } ,
2016-01-19 17:11:40 +00:00
replace : basic _replace ,
template : contact _format ,
} ;
this . attr ( 'autocomplete' , 'off' ) ;
2016-02-02 21:33:14 +00:00
var a = this . textcomplete ( [ contacts , community ] , { className : 'acpopup' , maxCount : 100 , zIndex : 1020 , appendTo : '#nav-search-box' } ) ;
2016-01-19 17:11:40 +00:00
a . on ( 'textComplete:select' , function ( e , value , strategy ) { submit _form ( this ) ; } ) ;
} ;
} ) ( jQuery ) ;
( function ( $ ) {
$ . fn . contact _autocomplete = function ( backend _url , typ , autosubmit , onselect ) {
if ( typeof typ === 'undefined' ) typ = '' ;
if ( typeof autosubmit === 'undefined' ) autosubmit = false ;
// Autocomplete contacts
contacts = {
match : /(^)([^\n]+)$/ ,
index : 2 ,
search : function ( term , callback ) { contact _search ( term , callback , backend _url , typ ) ; } ,
replace : basic _replace ,
template : contact _format ,
} ;
this . attr ( 'autocomplete' , 'off' ) ;
var a = this . textcomplete ( [ contacts ] , { className : 'acpopup' , zIndex : 1020 } ) ;
if ( autosubmit )
a . on ( 'textComplete:select' , function ( e , value , strategy ) { submit _form ( this ) ; } ) ;
if ( typeof onselect !== 'undefined' )
a . on ( 'textComplete:select' , function ( e , value , strategy ) { onselect ( value ) ; } ) ;
} ;
} ) ( jQuery ) ;
( function ( $ ) {
$ . fn . name _autocomplete = function ( backend _url , typ , autosubmit , onselect ) {
if ( typeof typ === 'undefined' ) typ = '' ;
if ( typeof autosubmit === 'undefined' ) autosubmit = false ;
// Autocomplete contacts
names = {
match : /(^)([^\n]+)$/ ,
index : 2 ,
search : function ( term , callback ) { contact _search ( term , callback , backend _url , typ ) ; } ,
replace : trim _replace ,
template : contact _format ,
} ;
this . attr ( 'autocomplete' , 'off' ) ;
var a = this . textcomplete ( [ names ] , { className : 'acpopup' , zIndex : 1020 } ) ;
if ( autosubmit )
a . on ( 'textComplete:select' , function ( e , value , strategy ) { submit _form ( this ) ; } ) ;
if ( typeof onselect !== 'undefined' )
a . on ( 'textComplete:select' , function ( e , value , strategy ) { onselect ( value ) ; } ) ;
} ;
} ) ( jQuery ) ;
2016-01-21 12:28:29 +00:00
/ * *
* Friendica people autocomplete legacy
* code which is needed for tinymce
*
* require jQuery , jquery . textareas
* /
function ACPopup ( elm , backend _url ) {
this . idsel = - 1 ;
this . element = elm ;
this . searchText = "" ;
this . ready = true ;
this . kp _timer = false ;
this . url = backend _url ;
this . conversation _id = null ;
var conv _id = this . element . id . match ( /\d+$/ ) ;
if ( conv _id ) this . conversation _id = conv _id [ 0 ] ;
console . log ( "ACPopup elm id" , this . element . id , "conversation" , this . conversation _id ) ;
var w = 530 ;
var h = 130 ;
if ( tinyMCE . activeEditor == null ) {
style = $ ( elm ) . offset ( ) ;
w = $ ( elm ) . width ( ) ;
h = $ ( elm ) . height ( ) ;
}
else {
// I can't find an "official" way to get the element who get all
// this fraking thing that is tinyMCE.
// This code will broke again at some point...
var container = $ ( tinyMCE . activeEditor . getContainer ( ) ) . find ( "table" ) ;
style = $ ( container ) . offset ( ) ;
w = $ ( container ) . width ( ) ;
h = $ ( container ) . height ( ) ;
}
style . top = style . top + h ;
style . width = w ;
style . position = 'absolute' ;
/ * s t y l e [ ' m a x - h e i g h t ' ] = ' 1 5 0 p x ' ;
style . border = '1px solid red' ;
style . background = '#cccccc' ;
style . overflow = 'auto' ;
style [ 'z-index' ] = '100000' ;
* /
style . display = 'none' ;
this . cont = $ ( "<div class='acpopup-mce'></div>" ) ;
this . cont . css ( style ) ;
$ ( "body" ) . append ( this . cont ) ;
}
ACPopup . prototype . close = function ( ) {
$ ( this . cont ) . remove ( ) ;
this . ready = false ;
}
ACPopup . prototype . search = function ( text ) {
var that = this ;
this . searchText = text ;
if ( this . kp _timer ) clearTimeout ( this . kp _timer ) ;
this . kp _timer = setTimeout ( function ( ) { that . _search ( ) ; } , 500 ) ;
}
ACPopup . prototype . _search = function ( ) {
console . log ( "_search" ) ;
var that = this ;
var postdata = {
start : 0 ,
count : 100 ,
search : this . searchText ,
type : 'c' ,
conversation : this . conversation _id ,
}
$ . ajax ( {
type : 'POST' ,
url : this . url ,
data : postdata ,
dataType : 'json' ,
success : function ( data ) {
that . cont . html ( "" ) ;
if ( data . tot > 0 ) {
that . cont . show ( ) ;
$ ( data . items ) . each ( function ( ) {
var html = "<img src='{0}' height='16px' width='16px'>{1} ({2})" . format ( this . photo , this . name , this . nick ) ;
var nick = this . nick . replace ( ' ' , '' ) ;
if ( this . id !== '' ) nick += '+' + this . id ;
that . add ( html , nick + ' - ' + this . link ) ;
} ) ;
} else {
that . cont . hide ( ) ;
}
}
} ) ;
}
ACPopup . prototype . add = function ( label , value ) {
var that = this ;
var elm = $ ( "<div class='acpopupitem' title='" + value + "'>" + label + "</div>" ) ;
elm . click ( function ( e ) {
t = $ ( this ) . attr ( 'title' ) . replace ( new RegExp ( ' \- .*' ) , '' ) ;
if ( typeof ( that . element . container ) === "undefined" ) {
el = $ ( that . element ) ;
sel = el . getSelection ( ) ;
sel . start = sel . start - that . searchText . length ;
el . setSelection ( sel . start , sel . end ) . replaceSelectedText ( t + ' ' ) . collapseSelection ( false ) ;
that . close ( ) ;
}
else {
txt = tinyMCE . activeEditor . getContent ( ) ;
// alert(that.searchText + ':' + t);
newtxt = txt . replace ( '@' + that . searchText , '@' + t + ' ' ) ;
tinyMCE . activeEditor . setContent ( newtxt ) ;
tinyMCE . activeEditor . focus ( ) ;
that . close ( ) ;
}
} ) ;
$ ( this . cont ) . append ( elm ) ;
}
ACPopup . prototype . onkey = function ( event ) {
if ( event . keyCode == '13' ) {
if ( this . idsel > - 1 ) {
this . cont . children ( ) [ this . idsel ] . click ( ) ;
event . preventDefault ( ) ;
}
else
this . close ( ) ;
}
if ( event . keyCode == '38' ) { //cursor up
cmax = this . cont . children ( ) . size ( ) - 1 ;
this . idsel -- ;
if ( this . idsel < 0 ) this . idsel = cmax ;
event . preventDefault ( ) ;
}
if ( event . keyCode == '40' || event . keyCode == '9' ) { //cursor down
cmax = this . cont . children ( ) . size ( ) - 1 ;
this . idsel ++ ;
if ( this . idsel > cmax ) this . idsel = 0 ;
event . preventDefault ( ) ;
}
if ( event . keyCode == '38' || event . keyCode == '40' || event . keyCode == '9' ) {
this . cont . children ( ) . removeClass ( 'selected' ) ;
$ ( this . cont . children ( ) [ this . idsel ] ) . addClass ( 'selected' ) ;
}
if ( event . keyCode == '27' ) { //ESC
this . close ( ) ;
}
}