Port autocomplete from tagInput to jQuery UI, send Last-Modified header and look for it in JS.
This commit is contained in:
parent
e5495e7df8
commit
c44a94e8f5
|
@ -34,6 +34,8 @@ if (!defined('STATUSNET')) {
|
|||
class PeopletagautocompleteAction extends Action
|
||||
{
|
||||
var $user;
|
||||
var $tags;
|
||||
var $last_mod;
|
||||
|
||||
/**
|
||||
* Check pre-requisites and instantiate attributes
|
||||
|
@ -66,13 +68,47 @@ class PeopletagautocompleteAction extends Action
|
|||
return false;
|
||||
}
|
||||
|
||||
$profile = $this->user->getProfile();
|
||||
$tags = $profile->getOwnedTags(common_current_user());
|
||||
|
||||
$this->tags = array();
|
||||
while ($tags->fetch()) {
|
||||
|
||||
if (empty($this->last_mod)) {
|
||||
$this->last_mod = $tags->modified;
|
||||
}
|
||||
|
||||
$arr = array();
|
||||
$arr['tag'] = $tags->tag;
|
||||
$arr['mode'] = $tags->private ? 'private' : 'public';
|
||||
// $arr['url'] = $tags->homeUrl();
|
||||
$arr['freq'] = $tags->taggedCount();
|
||||
|
||||
$this->tags[] = $arr;
|
||||
}
|
||||
|
||||
$tags->free();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Last modified time
|
||||
*
|
||||
* Helps in browser-caching
|
||||
*
|
||||
* @return String time
|
||||
*/
|
||||
|
||||
function lastModified()
|
||||
{
|
||||
return strtotime($this->last_mod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle request
|
||||
*
|
||||
* Does the subscription and returns results.
|
||||
* Print the JSON autocomplete data
|
||||
*
|
||||
* @param Array $args unused.
|
||||
*
|
||||
|
@ -81,24 +117,11 @@ class PeopletagautocompleteAction extends Action
|
|||
|
||||
function handle($args)
|
||||
{
|
||||
$profile = $this->user->getProfile();
|
||||
$tags = $profile->getOwnedTags(common_current_user());
|
||||
|
||||
$tags_array = array();
|
||||
while ($tags->fetch()) {
|
||||
$arr = array();
|
||||
$arr['tag'] = $tags->tag;
|
||||
$arr['mode'] = $tags->private ? 'private' : 'public';
|
||||
// $arr['url'] = $tags->homeUrl();
|
||||
$arr['freq'] = $tags->taggedCount();
|
||||
|
||||
$tags_array[] = $arr;
|
||||
//common_log(LOG_DEBUG, 'Autocomplete data: ' . json_encode($this->tags));
|
||||
if ($this->tags) {
|
||||
print(json_encode($this->tags));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$tags->free();
|
||||
|
||||
//common_log(LOG_DEBUG, 'Autocomplete data: ' . json_encode($tags_array));
|
||||
print(json_encode($tags_array));
|
||||
exit(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,381 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2009 Open Lab, http://www.open-lab.com/
|
||||
Written by Roberto Bicchierai http://roberto.open-lab.com.
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/**
|
||||
* options.tags an object array [{tag:"tag1",freq:1},{tag:"tag2",freq:2}, {tag:"tag3",freq:3},{tag:"tag4",freq:4} ].
|
||||
* options.jsonUrl an url returning a json object array in the same format of options.tag. The url will be called with
|
||||
* "search" parameter to be used server side to filter results
|
||||
* option.autoFilter true/false default=true when active show only matching tags, "false" should be used for server-side filtering
|
||||
* option.autoStart true/false default=false when active dropdown will appear entering field, otherwise when typing
|
||||
* options.sortBy "frequency"|"tag"|"none" default="tag"
|
||||
* options.tagSeparator default="," any separator char as space, comma, semicolumn
|
||||
* options.boldify true/false default trrue boldify the matching part of tag in dropdown
|
||||
*
|
||||
* options.suggestedTags callback an object array like ["sugtag1","sugtag2","sugtag3"]
|
||||
* options.suggestedTagsPlaceHolder jquery proxy for suggested tag placeholder. When placeholder is supplied (hence unique), tagField should be applied on a single input
|
||||
* (something like $("#myTagFiled").tagField(...) will works fine: $(":text").tagField(...) probably not!)
|
||||
*/
|
||||
|
||||
if (typeof(String.prototype.trim) == "undefined"){
|
||||
String.prototype.trim = function () {
|
||||
return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
jQuery.fn.tagInput = function(options) {
|
||||
// -------------------------- start default option values --------------------------
|
||||
if (!options.tags && !options.jsonUrl) {
|
||||
options.tags = [ { tag:"tag1", freq:1 }, { tag:"tag2", freq:2 }, { tag:"tag3", freq:3 }, { tag:"tag4", freq:4 } ];
|
||||
}
|
||||
|
||||
if (typeof(options.tagSeparator) == "undefined")
|
||||
options.tagSeparator = ",";
|
||||
|
||||
if (typeof(options.autoFilter) == "undefined")
|
||||
options.autoFilter = true;
|
||||
|
||||
if (typeof(options.autoStart) == "undefined")
|
||||
options.autoStart = false;
|
||||
|
||||
if (typeof(options.boldify) == "undefined")
|
||||
options.boldify = true;
|
||||
|
||||
if (typeof(options.animate) == "undefined")
|
||||
options.animate = true;
|
||||
|
||||
if (typeof(options.animate) != "function") {
|
||||
options._animate = options.animate;
|
||||
options.animate = function(show, el, cb) {
|
||||
var func = (options._animate) ? (show ? 'fadeIn' : 'fadeOut') : (show ? 'show' : 'hide');
|
||||
el[func](cb);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(options.sortBy) == "undefined")
|
||||
options.sortBy = "tag";
|
||||
|
||||
if (typeof(options.sortBy) == "string") {
|
||||
options._sortBy = options.sortBy;
|
||||
options.sortBy = function(obj) { return obj[options._sortBy]; }
|
||||
}
|
||||
|
||||
if (typeof(options.formatLine) == "undefined")
|
||||
options.formatLine = function (i, obj, search, matches) {
|
||||
var tag = obj.tag;
|
||||
if (options.boldify && matches) {
|
||||
tag = "<b>" + tag.substring(0, search.length) + "</b>" + tag.substring(search.length);
|
||||
}
|
||||
|
||||
var line = $("<div/>");
|
||||
line.append("<div class='tagInputLineTag'>" + tag + "</div>");
|
||||
if (obj.freq)
|
||||
line.append("<div class='tagInputLineFreq'>" + obj.freq + "</div>");
|
||||
return line;
|
||||
}
|
||||
|
||||
if (typeof(options.formatValue == "undefined"))
|
||||
options.formatValue = function (obj, i) {
|
||||
return obj.tag;
|
||||
}
|
||||
// -------------------------- end default option values --------------------------
|
||||
|
||||
|
||||
this.each(function() {
|
||||
|
||||
var theInput = $(this);
|
||||
var theDiv;
|
||||
|
||||
theInput.addClass("tagInput");
|
||||
theInput.tagOptions=options;
|
||||
theInput.attr('autocomplete', 'off');
|
||||
|
||||
var suggestedTagsPlaceHolder=options.suggestedTagsPlaceHolder;
|
||||
//create suggested tags place if the case
|
||||
if (options.suggestedTags){
|
||||
if (!suggestedTagsPlaceHolder){
|
||||
//create a placeholder
|
||||
var stl=$("<div class='tagInputSuggestedTags'><span class='label'>suggested tags: </span><span class='tagInputSuggestedTagList'></span></div>");
|
||||
suggestedTagsPlaceHolder=stl.find(".tagInputSuggestedTagList");
|
||||
theInput.after(stl);
|
||||
}
|
||||
|
||||
//fill with suggestions
|
||||
for (var tag in options.suggestedTags) {
|
||||
suggestedTagsPlaceHolder.append($("<span class='tag'>" + options.suggestedTags[tag] + "</span>"));
|
||||
}
|
||||
|
||||
// bind click on suggestion tags
|
||||
suggestedTagsPlaceHolder.find(".tag").click(function() {
|
||||
var element = $(this);
|
||||
var val = theInput.val();
|
||||
var tag = element.text();
|
||||
|
||||
//check if already present
|
||||
var re = new RegExp(tag + "\\b","g");
|
||||
if (containsTag(val, tag)) {
|
||||
val = val.replace(re, ""); //remove all the tag
|
||||
element.removeClass("tagUsed");
|
||||
} else {
|
||||
val = val + options.tagSeparator + tag;
|
||||
element.addClass("tagUsed");
|
||||
}
|
||||
theInput.val(refurbishTags(val));
|
||||
// selectSuggTagFromInput();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -------------------------- INPUT FOCUS --------------------------
|
||||
var tagInputFocus = function () {
|
||||
theDiv = $("#__tagInputDiv");
|
||||
// check if the result box exists
|
||||
if (theDiv.size() <= 0) {
|
||||
//create the div
|
||||
theDiv = $("<div id='__tagInputDiv' class='tagInputDiv' style='width:" + theInput.get(0).clientWidth + ";display:none; '></div>");
|
||||
theInput.after(theDiv);
|
||||
theDiv.css({left:theInput.position().left});
|
||||
}
|
||||
if (options.autoStart)
|
||||
tagInputRefreshDiv(theInput, theDiv);
|
||||
};
|
||||
|
||||
|
||||
// -------------------------- INPUT BLUR --------------------------
|
||||
var tagInputBlur = function () {
|
||||
// reformat string
|
||||
theDiv = $("#__tagInputDiv");
|
||||
theInput.val(refurbishTags(theInput.val()));
|
||||
|
||||
options.animate(0, theDiv, function() {
|
||||
theDiv.remove();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// -------------------------- INPUT KEYBOARD --------------------------
|
||||
var tagInputKey = function (e) {
|
||||
var rows = theDiv.find("div.tagInputLine");
|
||||
var rowNum = rows.index(theDiv.find("div.tagInputSel"));
|
||||
|
||||
var ret = true;
|
||||
switch (e.which) {
|
||||
case 38: //up arrow
|
||||
rowNum = (rowNum < 1 ? 0 : rowNum - 1 );
|
||||
tagInputHLSCR(rows.eq(rowNum), true);
|
||||
break;
|
||||
|
||||
case 40: //down arrow
|
||||
rowNum = (rowNum < rows.size() - 1 ? rowNum + 1 : rows.size() - 1 );
|
||||
tagInputHLSCR(rows.eq(rowNum), false);
|
||||
break;
|
||||
|
||||
case 9: //tab
|
||||
case 13: //enter
|
||||
if (theDiv.is(":visible")){
|
||||
var theRow = rows.eq(rowNum);
|
||||
tagInputClickRow(theRow);
|
||||
ret = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 27: //esc
|
||||
options.animate(0, theDiv);
|
||||
break;
|
||||
|
||||
default:
|
||||
$(document).stopTime("tagInputRefresh");
|
||||
$(document).oneTime(400, "tagInputRefresh", function() {
|
||||
tagInputRefreshDiv();
|
||||
});
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------- TAG DIV HIGHLIGHT AND SCROLL --------------------------
|
||||
var tagInputHLSCR = function(theRowJQ, isUp) {
|
||||
if (theRowJQ.size() > 0) {
|
||||
var div = theDiv.get(0);
|
||||
var theRow = theRowJQ.get(0);
|
||||
if (isUp) {
|
||||
if (theDiv.scrollTop() > theRow.offsetTop) {
|
||||
theDiv.scrollTop(theRow.offsetTop);
|
||||
}
|
||||
} else {
|
||||
if ((theRow.offsetTop + theRow.offsetHeight) > (div.scrollTop + div.offsetHeight)) {
|
||||
div.scrollTop = theRow.offsetTop + theRow.offsetHeight - div.offsetHeight;
|
||||
}
|
||||
}
|
||||
theDiv.find("div.tagInputSel").removeClass("tagInputSel");
|
||||
theRowJQ.addClass("tagInputSel");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// -------------------------- TAG LINE CLICK --------------------------
|
||||
var tagInputClickRow = function(theRow) {
|
||||
|
||||
var lastComma = theInput.val().lastIndexOf(options.tagSeparator);
|
||||
var sep= lastComma<=0? (""):(options.tagSeparator+ (options.tagSeparator==" "?"":" "));
|
||||
var newVal = (theInput.val().substr(0, lastComma) + sep + theRow.attr('id').replace('val-','')).trim();
|
||||
theInput.val(newVal);
|
||||
theDiv.hide();
|
||||
$().oneTime(200, function() {
|
||||
theInput.focus();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// -------------------------- REFILL TAG BOX --------------------------
|
||||
var tagInputRefreshDiv = function () {
|
||||
|
||||
var lastComma = theInput.val().lastIndexOf(options.tagSeparator);
|
||||
var search = theInput.val().substr(lastComma + 1).trim();
|
||||
|
||||
|
||||
// -------------------------- FILLING THE DIV --------------------------
|
||||
var fillingCallbak = function(tags) {
|
||||
tags = tags.sort(function (a, b) {
|
||||
if (options.sortBy(a) < options.sortBy(b))
|
||||
return 1;
|
||||
if (options.sortBy(a) > options.sortBy(b))
|
||||
return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (var i in tags) {
|
||||
tags[i]._val = options.formatValue(tags[i], i);
|
||||
var el = tags[i];
|
||||
var matches = el._val.toLocaleLowerCase().indexOf(search.toLocaleLowerCase()) == 0;
|
||||
if (!options.autoFilter || matches) {
|
||||
var line = $(options.formatLine(i, el, search, matches));
|
||||
if (!line.is('.tagInputLine'))
|
||||
line = $("<div class='tagInputLine'></div>").append(line);
|
||||
line.attr('id', 'val-' + el._val);
|
||||
theDiv.append(line);
|
||||
}
|
||||
}
|
||||
if (theDiv.html()!=""){
|
||||
options.animate(true, theDiv);
|
||||
}
|
||||
|
||||
theDiv.find("div:first").addClass("tagInputSel");
|
||||
theDiv.find("div.tagInputLine").bind("click", function() {
|
||||
tagInputClickRow($(this));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
if (search != "" || options.autoStart) {
|
||||
theDiv.html("");
|
||||
|
||||
if (options.tags)
|
||||
fillingCallbak(options.tags);
|
||||
else{
|
||||
var data = {search:search};
|
||||
$.getJSON(options.jsonUrl, data, fillingCallbak );
|
||||
}
|
||||
} else {
|
||||
options.animate(false, theDiv);
|
||||
}
|
||||
};
|
||||
|
||||
// -------------------------- CLEAN THE TAG LIST FROM EXTRA SPACES, DOUBLE COMMAS ETC. --------------------------
|
||||
var refurbishTags = function (tagString) {
|
||||
var splitted = tagString.split(options.tagSeparator);
|
||||
var res = "";
|
||||
var first = true;
|
||||
for (var i = 0; i < splitted.length; i++) {
|
||||
if (splitted[i].trim() != "") {
|
||||
if (first) {
|
||||
first = false;
|
||||
res = res + splitted[i].trim();
|
||||
} else {
|
||||
res = res + options.tagSeparator+ (options.tagSeparator==" "?"":" ") + splitted[i].trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
return( res);
|
||||
};
|
||||
|
||||
// -------------------------- TEST IF TAG IS PRESENT --------------------------
|
||||
var containsTag=function (tagString,tag){
|
||||
var splitted = tagString.split(options.tagSeparator);
|
||||
var res="";
|
||||
var found=false;
|
||||
tag=tag.trim();
|
||||
for(i = 0; i < splitted.length; i++){
|
||||
var testTag=splitted[i].trim();
|
||||
if (testTag==tag){
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------- SELECT TAGS BASING ON USER INPUT --------------------------
|
||||
var delayedSelectTagFromInput= function(){
|
||||
var element = $(this);
|
||||
$().stopTime("suggTagRefresh");
|
||||
$().oneTime(200, "suggTagRefresh", function() {
|
||||
selectSuggTagFromInput();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var selectSuggTagFromInput = function () {
|
||||
var val = theInput.val();
|
||||
suggestedTagsPlaceHolder.find(".tag").each(function(){
|
||||
var el = $(this);
|
||||
var tag=el.text();
|
||||
|
||||
//check if already present
|
||||
if (containsTag(val,tag)) {
|
||||
el.addClass("tagUsed");
|
||||
} else {
|
||||
el.removeClass("tagUsed");
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// -------------------------- INPUT BINDINGS --------------------------
|
||||
$(this).bind("focus", tagInputFocus).bind("blur", tagInputBlur).bind("keydown", tagInputKey);
|
||||
if (options.suggestedTags)
|
||||
$(this).bind("keyup",delayedSelectTagFromInput) ;
|
||||
|
||||
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
/**
|
||||
* jQuery.timers - Timer abstractions for jQuery
|
||||
* Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com)
|
||||
* Licensed under the WTFPL (http://sam.zoy.org/wtfpl/).
|
||||
* Date: 2009/10/16
|
||||
*
|
||||
* @author Blair Mitchelmore
|
||||
* @version 1.2
|
||||
*
|
||||
**/
|
||||
|
||||
jQuery.fn.extend({
|
||||
everyTime: function(interval, label, fn, times) {
|
||||
return this.each(function() {
|
||||
jQuery.timer.add(this, interval, label, fn, times);
|
||||
});
|
||||
},
|
||||
oneTime: function(interval, label, fn) {
|
||||
return this.each(function() {
|
||||
jQuery.timer.add(this, interval, label, fn, 1);
|
||||
});
|
||||
},
|
||||
stopTime: function(label, fn) {
|
||||
return this.each(function() {
|
||||
jQuery.timer.remove(this, label, fn);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
jQuery.extend({
|
||||
timer: {
|
||||
global: [],
|
||||
guid: 1,
|
||||
dataKey: "jQuery.timer",
|
||||
regex: /^([0-9]+(?:\.[0-9]*)?)\s*(.*s)?$/,
|
||||
powers: {
|
||||
// Yeah this is major overkill...
|
||||
'ms': 1,
|
||||
'cs': 10,
|
||||
'ds': 100,
|
||||
's': 1000,
|
||||
'das': 10000,
|
||||
'hs': 100000,
|
||||
'ks': 1000000
|
||||
},
|
||||
timeParse: function(value) {
|
||||
if (value == undefined || value == null)
|
||||
return null;
|
||||
var result = this.regex.exec(jQuery.trim(value.toString()));
|
||||
if (result[2]) {
|
||||
var num = parseFloat(result[1]);
|
||||
var mult = this.powers[result[2]] || 1;
|
||||
return num * mult;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
add: function(element, interval, label, fn, times) {
|
||||
var counter = 0;
|
||||
|
||||
if (jQuery.isFunction(label)) {
|
||||
if (!times)
|
||||
times = fn;
|
||||
fn = label;
|
||||
label = interval;
|
||||
}
|
||||
|
||||
interval = jQuery.timer.timeParse(interval);
|
||||
|
||||
if (typeof interval != 'number' || isNaN(interval) || interval < 0)
|
||||
return;
|
||||
|
||||
if (typeof times != 'number' || isNaN(times) || times < 0)
|
||||
times = 0;
|
||||
|
||||
times = times || 0;
|
||||
|
||||
var timers = jQuery.data(element, this.dataKey) || jQuery.data(element, this.dataKey, {});
|
||||
|
||||
if (!timers[label])
|
||||
timers[label] = {};
|
||||
|
||||
fn.timerID = fn.timerID || this.guid++;
|
||||
|
||||
var handler = function() {
|
||||
if ((++counter > times && times !== 0) || fn.call(element, counter) === false)
|
||||
jQuery.timer.remove(element, label, fn);
|
||||
};
|
||||
|
||||
handler.timerID = fn.timerID;
|
||||
|
||||
if (!timers[label][fn.timerID])
|
||||
timers[label][fn.timerID] = window.setInterval(handler,interval);
|
||||
|
||||
this.global.push( element );
|
||||
|
||||
},
|
||||
remove: function(element, label, fn) {
|
||||
var timers = jQuery.data(element, this.dataKey), ret;
|
||||
|
||||
if ( timers ) {
|
||||
|
||||
if (!label) {
|
||||
for ( label in timers )
|
||||
this.remove(element, label, fn);
|
||||
} else if ( timers[label] ) {
|
||||
if ( fn ) {
|
||||
if ( fn.timerID ) {
|
||||
window.clearInterval(timers[label][fn.timerID]);
|
||||
delete timers[label][fn.timerID];
|
||||
}
|
||||
} else {
|
||||
for ( var fn in timers[label] ) {
|
||||
window.clearInterval(timers[label][fn]);
|
||||
delete timers[label][fn];
|
||||
}
|
||||
}
|
||||
|
||||
for ( ret in timers[label] ) break;
|
||||
if ( !ret ) {
|
||||
ret = null;
|
||||
delete timers[label];
|
||||
}
|
||||
}
|
||||
|
||||
for ( ret in timers ) break;
|
||||
if ( !ret )
|
||||
jQuery.removeData(element, this.dataKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
jQuery(window).bind("unload", function() {
|
||||
jQuery.each(jQuery.timer.global, function(index, item) {
|
||||
jQuery.timer.remove(item);
|
||||
});
|
||||
});
|
99
js/util.js
99
js/util.js
|
@ -518,8 +518,8 @@ var SN = { // StatusNet
|
|||
url: form.attr('action'),
|
||||
data: form.serialize() + '&ajax=1',
|
||||
beforeSend: function(xhr) {
|
||||
form.addClass(SN.C.S.Processing)
|
||||
.find('.submit')
|
||||
form.find('.submit')
|
||||
.addClass(SN.C.S.Processing)
|
||||
.addClass(SN.C.S.Disabled)
|
||||
.attr(SN.C.S.Disabled, SN.C.S.Disabled);
|
||||
},
|
||||
|
@ -1543,39 +1543,94 @@ var SN = { // StatusNet
|
|||
});
|
||||
},
|
||||
|
||||
PeopletagAutocomplete: function() {
|
||||
$('.form_tag_user #tags').tagInput({
|
||||
tags: SN.C.PtagACData,
|
||||
tagSeparator: " ",
|
||||
animate: false,
|
||||
formatLine: function (i, e, search, matches) {
|
||||
var tag = "<b>" + e.tag.substring(0, search.length) + "</b>" + e.tag.substring(search.length);
|
||||
/**
|
||||
* Called when a people tag edit box is shown in the interface
|
||||
*
|
||||
* - loads the jQuery UI autocomplete plugin
|
||||
* - sets event handlers for tag completion
|
||||
*
|
||||
*/
|
||||
PeopletagAutocomplete: function(txtBox) {
|
||||
var split = function(val) {
|
||||
return val.split( /\s+/ );
|
||||
}
|
||||
var extractLast = function(term) {
|
||||
return split(term).pop();
|
||||
}
|
||||
|
||||
var line = $("<div/>").addClass('mode-' + e.mode);
|
||||
line.append($("<div class='tagInputLineTag'>" + tag
|
||||
+ " <em class='privacy_mode'>" + e.mode + "</em></div>"));
|
||||
if (e.freq)
|
||||
line.append("<div class='tagInputLineFreq'>" + e.freq + "</div>");
|
||||
return line;
|
||||
// don't navigate away from the field on tab when selecting an item
|
||||
txtBox.live( "keydown", function( event ) {
|
||||
if ( event.keyCode === $.ui.keyCode.TAB &&
|
||||
$(this).data( "autocomplete" ).menu.active ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
}).autocomplete({
|
||||
minLength: 0,
|
||||
source: function(request, response) {
|
||||
// delegate back to autocomplete, but extract the last term
|
||||
response($.ui.autocomplete.filter(
|
||||
SN.C.PtagACData, extractLast(request.term)));
|
||||
},
|
||||
focus: function() {
|
||||
return false;
|
||||
},
|
||||
select: function(event, ui) {
|
||||
var terms = split(this.value);
|
||||
terms.pop();
|
||||
terms.push(ui.item.value);
|
||||
terms.push("");
|
||||
this.value = terms.join(" ");
|
||||
return false;
|
||||
}
|
||||
}).data('autocomplete')._renderItem = function(ul, item) {
|
||||
// FIXME: with jQuery UI you cannot have it highlight the match
|
||||
var _l = '<a class="ptag-ac-line-tag">' + item.tag
|
||||
+ ' <em class="privacy_mode">' + item.mode + '</em>'
|
||||
+ '<span class="freq">' + item.freq + '</span></a>'
|
||||
|
||||
return $("<li/>")
|
||||
.addClass('mode-' + item.mode)
|
||||
.addClass('ptag-ac-line')
|
||||
.data("item.autocomplete", item)
|
||||
.append(_l)
|
||||
.appendTo(ul);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Run setup for the ajax people tags editor
|
||||
*
|
||||
* - show edit button
|
||||
* - set event handle for click on edit button
|
||||
* - loads people tag autocompletion data if not already present
|
||||
* or if it is stale.
|
||||
*
|
||||
*/
|
||||
PeopleTags: function() {
|
||||
$('.user_profile_tags .editable').append($('<button class="peopletags_edit_button"/>'));
|
||||
|
||||
$('.peopletags_edit_button').live('click', function() {
|
||||
var form = $(this).parents('dd').eq(0).find('form');
|
||||
// We can buy time from the above animation
|
||||
if (typeof SN.C.PtagACData === 'undefined') {
|
||||
$.getJSON(_peopletagAC + '?token=' + $('#token').val(), function(data) {
|
||||
|
||||
$.ajax({
|
||||
url: _peopletagAC,
|
||||
dataType: 'json',
|
||||
data: {token: $('#token').val()},
|
||||
ifModified: true,
|
||||
success: function(data) {
|
||||
// item.label is used to match
|
||||
for (i=0; i < data.length; i++) {
|
||||
data[i].label = data[i].tag;
|
||||
}
|
||||
|
||||
SN.C.PtagACData = data;
|
||||
_loadTagInput(SN.Init.PeopletagAutocomplete);
|
||||
});
|
||||
} else { _loadTagInput(SN.Init.PeopletagAutocomplete); }
|
||||
SN.Init.PeopletagAutocomplete(form.find('#tags'));
|
||||
}
|
||||
});
|
||||
|
||||
$(this).parents('ul').eq(0).fadeOut(200, function() {form.fadeIn(200).find('input#tags')});
|
||||
})
|
||||
});
|
||||
|
||||
$('.user_profile_tags form .submit').live('click', function() {
|
||||
SN.U.FormPeopletagsXHR($(this).parents('form')); return false;
|
||||
|
|
2
js/util.min.js
vendored
2
js/util.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -330,9 +330,7 @@ class Action extends HTMLOutputter // lawsuit
|
|||
$this->script('geometa.js');
|
||||
|
||||
}
|
||||
$this->inlineScript('function _loadTagInput(init) { $.getScript("'.common_path('js/jquery.timers.js') .
|
||||
'"); $.getScript("'.common_path('js/jquery.tagInput.js').'", init); } var _peopletagAC = "' .
|
||||
|
||||
$this->inlineScript('var _peopletagAC = "' .
|
||||
common_local_url('peopletagautocomplete') . '";');
|
||||
$this->showScriptMessages();
|
||||
// Frame-busting code to avoid clickjacking attacks.
|
||||
|
|
|
@ -1915,31 +1915,29 @@ min-width:0;
|
|||
|
||||
/* tag autocomplete */
|
||||
|
||||
.tagInputDiv {
|
||||
display: none;
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
margin-top:-1px;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.tagInputLine {
|
||||
.ptag-ac-line {
|
||||
font-weight: normal;
|
||||
padding:4px;
|
||||
background-color: white;
|
||||
}
|
||||
.ptag-ac-line:nth-child(odd) {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.tagInputLineTag {
|
||||
.ptag-ac-line-tag {
|
||||
min-width: 150px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tagInputLineFreq {
|
||||
.ptag-ac-line .freq {
|
||||
min-width: 50px;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
float:right;
|
||||
}
|
||||
|
||||
.ptag-ac-line.mode-public .privacy_mode {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.inline-attachment img {
|
||||
/* Why on earth is this changed to block at the top? */
|
||||
display: inline;
|
||||
|
|
|
@ -2173,46 +2173,30 @@ background-position: 0px -1978px;
|
|||
|
||||
/* tag autocomplete */
|
||||
|
||||
.tagInputDiv {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color:#f1f1f1;
|
||||
overflow: auto;
|
||||
margin-top:-1px;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.tagInputLine {
|
||||
.ptag-ac-line {
|
||||
font-weight: normal;
|
||||
padding:4px;
|
||||
background-color: white;
|
||||
min-height:30px;
|
||||
}
|
||||
.ptag-ac-line:nth-child(odd) {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.tagInputLineTag {
|
||||
.ptag-ac-line-tag {
|
||||
min-width: 150px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tagInputLineFreq {
|
||||
.ptag-ac-line .freq {
|
||||
min-width: 50px;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
float:right;
|
||||
}
|
||||
|
||||
.tagInputDiv {
|
||||
background-color: white;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
.tagInputDiv .mode-public .privacy_mode {
|
||||
.ptag-ac-line.mode-public .privacy_mode {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.tagInputSel {
|
||||
background-color: gray;
|
||||
color:white;
|
||||
}
|
||||
|
||||
/*end of @media screen, projection, tv*/
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user