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
|
class PeopletagautocompleteAction extends Action
|
||||||
{
|
{
|
||||||
var $user;
|
var $user;
|
||||||
|
var $tags;
|
||||||
|
var $last_mod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check pre-requisites and instantiate attributes
|
* Check pre-requisites and instantiate attributes
|
||||||
@ -66,13 +68,47 @@ class PeopletagautocompleteAction extends Action
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last modified time
|
||||||
|
*
|
||||||
|
* Helps in browser-caching
|
||||||
|
*
|
||||||
|
* @return String time
|
||||||
|
*/
|
||||||
|
|
||||||
|
function lastModified()
|
||||||
|
{
|
||||||
|
return strtotime($this->last_mod);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle request
|
* Handle request
|
||||||
*
|
*
|
||||||
* Does the subscription and returns results.
|
* Print the JSON autocomplete data
|
||||||
*
|
*
|
||||||
* @param Array $args unused.
|
* @param Array $args unused.
|
||||||
*
|
*
|
||||||
@ -81,24 +117,11 @@ class PeopletagautocompleteAction extends Action
|
|||||||
|
|
||||||
function handle($args)
|
function handle($args)
|
||||||
{
|
{
|
||||||
$profile = $this->user->getProfile();
|
//common_log(LOG_DEBUG, 'Autocomplete data: ' . json_encode($this->tags));
|
||||||
$tags = $profile->getOwnedTags(common_current_user());
|
if ($this->tags) {
|
||||||
|
print(json_encode($this->tags));
|
||||||
$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;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tags->free();
|
|
||||||
|
|
||||||
//common_log(LOG_DEBUG, 'Autocomplete data: ' . json_encode($tags_array));
|
|
||||||
print(json_encode($tags_array));
|
|
||||||
exit(0);
|
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'),
|
url: form.attr('action'),
|
||||||
data: form.serialize() + '&ajax=1',
|
data: form.serialize() + '&ajax=1',
|
||||||
beforeSend: function(xhr) {
|
beforeSend: function(xhr) {
|
||||||
form.addClass(SN.C.S.Processing)
|
form.find('.submit')
|
||||||
.find('.submit')
|
.addClass(SN.C.S.Processing)
|
||||||
.addClass(SN.C.S.Disabled)
|
.addClass(SN.C.S.Disabled)
|
||||||
.attr(SN.C.S.Disabled, 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({
|
* Called when a people tag edit box is shown in the interface
|
||||||
tags: SN.C.PtagACData,
|
*
|
||||||
tagSeparator: " ",
|
* - loads the jQuery UI autocomplete plugin
|
||||||
animate: false,
|
* - sets event handlers for tag completion
|
||||||
formatLine: function (i, e, search, matches) {
|
*
|
||||||
var tag = "<b>" + e.tag.substring(0, search.length) + "</b>" + e.tag.substring(search.length);
|
*/
|
||||||
|
PeopletagAutocomplete: function(txtBox) {
|
||||||
var line = $("<div/>").addClass('mode-' + e.mode);
|
var split = function(val) {
|
||||||
line.append($("<div class='tagInputLineTag'>" + tag
|
return val.split( /\s+/ );
|
||||||
+ " <em class='privacy_mode'>" + e.mode + "</em></div>"));
|
}
|
||||||
if (e.freq)
|
var extractLast = function(term) {
|
||||||
line.append("<div class='tagInputLineFreq'>" + e.freq + "</div>");
|
return split(term).pop();
|
||||||
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() {
|
PeopleTags: function() {
|
||||||
$('.user_profile_tags .editable').append($('<button class="peopletags_edit_button"/>'));
|
$('.user_profile_tags .editable').append($('<button class="peopletags_edit_button"/>'));
|
||||||
|
|
||||||
$('.peopletags_edit_button').live('click', function() {
|
$('.peopletags_edit_button').live('click', function() {
|
||||||
var form = $(this).parents('dd').eq(0).find('form');
|
var form = $(this).parents('dd').eq(0).find('form');
|
||||||
// We can buy time from the above animation
|
// 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;
|
SN.C.PtagACData = data;
|
||||||
_loadTagInput(SN.Init.PeopletagAutocomplete);
|
SN.Init.PeopletagAutocomplete(form.find('#tags'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else { _loadTagInput(SN.Init.PeopletagAutocomplete); }
|
|
||||||
|
|
||||||
$(this).parents('ul').eq(0).fadeOut(200, function() {form.fadeIn(200).find('input#tags')});
|
$(this).parents('ul').eq(0).fadeOut(200, function() {form.fadeIn(200).find('input#tags')});
|
||||||
})
|
});
|
||||||
|
|
||||||
$('.user_profile_tags form .submit').live('click', function() {
|
$('.user_profile_tags form .submit').live('click', function() {
|
||||||
SN.U.FormPeopletagsXHR($(this).parents('form')); return false;
|
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->script('geometa.js');
|
||||||
|
|
||||||
}
|
}
|
||||||
$this->inlineScript('function _loadTagInput(init) { $.getScript("'.common_path('js/jquery.timers.js') .
|
$this->inlineScript('var _peopletagAC = "' .
|
||||||
'"); $.getScript("'.common_path('js/jquery.tagInput.js').'", init); } var _peopletagAC = "' .
|
|
||||||
|
|
||||||
common_local_url('peopletagautocomplete') . '";');
|
common_local_url('peopletagautocomplete') . '";');
|
||||||
$this->showScriptMessages();
|
$this->showScriptMessages();
|
||||||
// Frame-busting code to avoid clickjacking attacks.
|
// Frame-busting code to avoid clickjacking attacks.
|
||||||
|
@ -1915,31 +1915,29 @@ min-width:0;
|
|||||||
|
|
||||||
/* tag autocomplete */
|
/* tag autocomplete */
|
||||||
|
|
||||||
.tagInputDiv {
|
.ptag-ac-line {
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
overflow: auto;
|
|
||||||
margin-top:-1px;
|
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tagInputLine {
|
|
||||||
font-weight: normal;
|
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;
|
min-width: 150px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagInputLineFreq {
|
.ptag-ac-line .freq {
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
display: inline-block;
|
|
||||||
float:right;
|
float:right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ptag-ac-line.mode-public .privacy_mode {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
.inline-attachment img {
|
.inline-attachment img {
|
||||||
/* Why on earth is this changed to block at the top? */
|
/* Why on earth is this changed to block at the top? */
|
||||||
display: inline;
|
display: inline;
|
||||||
|
@ -2173,46 +2173,30 @@ background-position: 0px -1978px;
|
|||||||
|
|
||||||
/* tag autocomplete */
|
/* tag autocomplete */
|
||||||
|
|
||||||
.tagInputDiv {
|
.ptag-ac-line {
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
background-color:#f1f1f1;
|
|
||||||
overflow: auto;
|
|
||||||
margin-top:-1px;
|
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tagInputLine {
|
|
||||||
font-weight: normal;
|
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;
|
min-width: 150px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagInputLineFreq {
|
.ptag-ac-line .freq {
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
display: inline-block;
|
|
||||||
float:right;
|
float:right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagInputDiv {
|
.ptag-ac-line.mode-public .privacy_mode {
|
||||||
background-color: white;
|
|
||||||
border: 1px solid lightgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tagInputDiv .mode-public .privacy_mode {
|
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagInputSel {
|
|
||||||
background-color: gray;
|
|
||||||
color:white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*end of @media screen, projection, tv*/
|
/*end of @media screen, projection, tv*/
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user