From 16e4e94b293035ab73e00c5d6cd68f54797e23cd Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Tue, 19 Jan 2016 18:11:40 +0100 Subject: [PATCH 01/17] rework autocomplete: initial commit --- js/autocomplete.js | 192 +++ js/fk.autocomplete.js | 2 +- js/main.js | 2 +- library/jquery-textcomplete/LICENSE | 21 + .../jquery.textcomplete.js | 1104 +++++++++++++++++ view/templates/display-head.tpl | 4 +- view/templates/head.tpl | 4 +- view/templates/jot-header.tpl | 2 +- 8 files changed, 1325 insertions(+), 6 deletions(-) create mode 100644 js/autocomplete.js create mode 100644 library/jquery-textcomplete/LICENSE create mode 100644 library/jquery-textcomplete/jquery.textcomplete.js diff --git a/js/autocomplete.js b/js/autocomplete.js new file mode 100644 index 000000000..aa4494b71 --- /dev/null +++ b/js/autocomplete.js @@ -0,0 +1,192 @@ +/** + * Red people autocomplete + * + * require jQuery, jquery.textcomplete + */ +function contact_search(term, callback, backend_url, type) { + + // 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 + matching.unshift({taggable:false, text: term, replace: term}); + setTimeout(function() { callback(matching); } , 1); // Use "pseudo-thread" to avoid some problems + return; + } + } + + var postdata = { + start:0, + count:100, + search:term, + type:type, + }; + + + $.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); + if(typeof desc === 'undefined') desc = ''; + if(desc) desc = ' ('+desc+')'; + return "
{2}{3}
".format(item.taggable, item.photo, item.name, desc, item.link); + } + else + return "
" + item.text + "
"; +} + +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) { + + // 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, + }; + + smilies = { + match: /(^|\s)(:[a-z]{2,})$/, + index: 2, + 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; }, + replace: function(item) { return "$1" + item.text + ' '; }, + }; + this.attr('autocomplete','off'); + this.textcomplete([contacts,smilies], {className:'acpopup', zIndex:1020}); + }; +})( jQuery ); + +/** + * jQuery plugin 'search_autocomplete' + */ +(function( $ ) { + $.fn.search_autocomplete = function(backend_url) { + // Autocomplete contacts + contacts = { + match: /(^@)([^\n]{2,})$/, + index: 2, + search: function(term, callback) { contact_search(term, callback, backend_url, 'x'); }, + replace: basic_replace, + template: contact_format, + }; + this.attr('autocomplete', 'off'); + var a = this.textcomplete([contacts], {className:'acpopup', maxCount:100, zIndex: 1020, appendTo:'nav'}); + 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 ); diff --git a/js/fk.autocomplete.js b/js/fk.autocomplete.js index 6010578ab..d7c81276b 100644 --- a/js/fk.autocomplete.js +++ b/js/fk.autocomplete.js @@ -86,7 +86,7 @@ ACPopup.prototype._search = function(){ if (data.tot>0){ that.cont.show(); $(data.items).each(function(){ - var html = "{1} ({2})".format(this.photo, this.name, this.nick); + var html = " {1} ({2})".format(this.photo, this.name, this.addr); var nick = this.nick.replace(' ',''); if (this.id!=='') nick += '+' + this.id; that.add(html, nick + ' - ' + this.link); diff --git a/js/main.js b/js/main.js index 2e65ef231..1c1d07d52 100644 --- a/js/main.js +++ b/js/main.js @@ -493,7 +493,7 @@ $('body').css('cursor', 'auto'); } /* autocomplete @nicknames */ - $(".comment-edit-form textarea").contact_autocomplete(baseurl+"/acl"); + $(".comment-edit-form textarea").editor_autocomplete(baseurl+"/acl"); // setup videos, since VideoJS won't take care of any loaded via AJAX if(typeof videojs != 'undefined') videojs.autoSetup(); diff --git a/library/jquery-textcomplete/LICENSE b/library/jquery-textcomplete/LICENSE new file mode 100644 index 000000000..4848bd637 --- /dev/null +++ b/library/jquery-textcomplete/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2014 Yuku Takahashi + +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. diff --git a/library/jquery-textcomplete/jquery.textcomplete.js b/library/jquery-textcomplete/jquery.textcomplete.js new file mode 100644 index 000000000..3df84f3b4 --- /dev/null +++ b/library/jquery-textcomplete/jquery.textcomplete.js @@ -0,0 +1,1104 @@ +/*! + * jQuery.textcomplete + * + * Repository: https://github.com/yuku-t/jquery-textcomplete + * License: MIT (https://github.com/yuku-t/jquery-textcomplete/blob/master/LICENSE) + * Author: Yuku Takahashi + */ + +if (typeof jQuery === 'undefined') { + throw new Error('jQuery.textcomplete requires jQuery'); +} + ++function ($) { + 'use strict'; + + var warn = function (message) { + if (console.warn) { console.warn(message); } + }; + + $.fn.textcomplete = function (strategies, option) { + var args = Array.prototype.slice.call(arguments); + return this.each(function () { + var $this = $(this); + var completer = $this.data('textComplete'); + if (!completer) { + completer = new $.fn.textcomplete.Completer(this, option || {}); + $this.data('textComplete', completer); + } + if (typeof strategies === 'string') { + if (!completer) return; + args.shift() + completer[strategies].apply(completer, args); + if (strategies === 'destroy') { + $this.removeData('textComplete'); + } + } else { + // For backward compatibility. + // TODO: Remove at v0.4 + $.each(strategies, function (obj) { + $.each(['header', 'footer', 'placement', 'maxCount'], function (name) { + if (obj[name]) { + completer.option[name] = obj[name]; + warn(name + 'as a strategy param is deprecated. Use option.'); + delete obj[name]; + } + }); + }); + completer.register($.fn.textcomplete.Strategy.parse(strategies)); + } + }); + }; + +}(jQuery); + ++function ($) { + 'use strict'; + + // Exclusive execution control utility. + // + // func - The function to be locked. It is executed with a function named + // `free` as the first argument. Once it is called, additional + // execution are ignored until the free is invoked. Then the last + // ignored execution will be replayed immediately. + // + // Examples + // + // var lockedFunc = lock(function (free) { + // setTimeout(function { free(); }, 1000); // It will be free in 1 sec. + // console.log('Hello, world'); + // }); + // lockedFunc(); // => 'Hello, world' + // lockedFunc(); // none + // lockedFunc(); // none + // // 1 sec past then + // // => 'Hello, world' + // lockedFunc(); // => 'Hello, world' + // lockedFunc(); // none + // + // Returns a wrapped function. + var lock = function (func) { + var locked, queuedArgsToReplay; + + return function () { + // Convert arguments into a real array. + var args = Array.prototype.slice.call(arguments); + if (locked) { + // Keep a copy of this argument list to replay later. + // OK to overwrite a previous value because we only replay + // the last one. + queuedArgsToReplay = args; + return; + } + locked = true; + var self = this; + args.unshift(function replayOrFree() { + if (queuedArgsToReplay) { + // Other request(s) arrived while we were locked. + // Now that the lock is becoming available, replay + // the latest such request, then call back here to + // unlock (or replay another request that arrived + // while this one was in flight). + var replayArgs = queuedArgsToReplay; + queuedArgsToReplay = undefined; + replayArgs.unshift(replayOrFree); + func.apply(self, replayArgs); + } else { + locked = false; + } + }); + func.apply(this, args); + }; + }; + + var isString = function (obj) { + return Object.prototype.toString.call(obj) === '[object String]'; + }; + + var uniqueId = 0; + + function Completer(element, option) { + this.$el = $(element); + this.id = 'textcomplete' + uniqueId++; + this.strategies = []; + this.views = []; + this.option = $.extend({}, Completer._getDefaults(), option); + + if (!this.$el.is('input[type=text]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') { + throw new Error('textcomplete must be called on a Textarea or a ContentEditable.'); + } + + if (element === document.activeElement) { + // element has already been focused. Initialize view objects immediately. + this.initialize() + } else { + // Initialize view objects lazily. + var self = this; + this.$el.one('focus.' + this.id, function () { self.initialize(); }); + } + } + + Completer._getDefaults = function () { + if (!Completer.DEFAULTS) { + Completer.DEFAULTS = { + appendTo: $('body'), + zIndex: '100' + }; + } + + return Completer.DEFAULTS; + } + + $.extend(Completer.prototype, { + // Public properties + // ----------------- + + id: null, + option: null, + strategies: null, + adapter: null, + dropdown: null, + $el: null, + + // Public methods + // -------------- + + initialize: function () { + var element = this.$el.get(0); + // Initialize view objects. + this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option); + var Adapter, viewName; + if (this.option.adapter) { + Adapter = this.option.adapter; + } else { + if (this.$el.is('textarea') || this.$el.is('input[type=text]')) { + viewName = typeof element.selectionEnd === 'number' ? 'Textarea' : 'IETextarea'; + } else { + viewName = 'ContentEditable'; + } + Adapter = $.fn.textcomplete[viewName]; + } + this.adapter = new Adapter(element, this, this.option); + }, + + destroy: function () { + this.$el.off('.' + this.id); + if (this.adapter) { + this.adapter.destroy(); + } + if (this.dropdown) { + this.dropdown.destroy(); + } + this.$el = this.adapter = this.dropdown = null; + }, + + // Invoke textcomplete. + trigger: function (text, skipUnchangedTerm) { + if (!this.dropdown) { this.initialize(); } + text != null || (text = this.adapter.getTextFromHeadToCaret()); + var searchQuery = this._extractSearchQuery(text); + if (searchQuery.length) { + var term = searchQuery[1]; + // Ignore shift-key, ctrl-key and so on. + if (skipUnchangedTerm && this._term === term) { return; } + this._term = term; + this._search.apply(this, searchQuery); + } else { + this._term = null; + this.dropdown.deactivate(); + } + }, + + fire: function (eventName) { + var args = Array.prototype.slice.call(arguments, 1); + this.$el.trigger(eventName, args); + return this; + }, + + register: function (strategies) { + Array.prototype.push.apply(this.strategies, strategies); + }, + + // Insert the value into adapter view. It is called when the dropdown is clicked + // or selected. + // + // value - The selected element of the array callbacked from search func. + // strategy - The Strategy object. + select: function (value, strategy) { + this.adapter.select(value, strategy); + this.fire('change').fire('textComplete:select', value, strategy); + this.adapter.focus(); + }, + + // Private properties + // ------------------ + + _clearAtNext: true, + _term: null, + + // Private methods + // --------------- + + // Parse the given text and extract the first matching strategy. + // + // Returns an array including the strategy, the query term and the match + // object if the text matches an strategy; otherwise returns an empty array. + _extractSearchQuery: function (text) { + for (var i = 0; i < this.strategies.length; i++) { + var strategy = this.strategies[i]; + var context = strategy.context(text); + if (context || context === '') { + if (isString(context)) { text = context; } + var match = text.match(strategy.match); + if (match) { return [strategy, match[strategy.index], match]; } + } + } + return [] + }, + + // Call the search method of selected strategy.. + _search: lock(function (free, strategy, term, match) { + var self = this; + strategy.search(term, function (data, stillSearching) { + if (!self.dropdown.shown) { + self.dropdown.activate(); + self.dropdown.setPosition(self.adapter.getCaretPosition()); + } + if (self._clearAtNext) { + // The first callback in the current lock. + self.dropdown.clear(); + self._clearAtNext = false; + } + self.dropdown.render(self._zip(data, strategy)); + if (!stillSearching) { + // The last callback in the current lock. + free(); + self._clearAtNext = true; // Call dropdown.clear at the next time. + } + }, match); + }), + + // Build a parameter for Dropdown#render. + // + // Examples + // + // this._zip(['a', 'b'], 's'); + // //=> [{ value: 'a', strategy: 's' }, { value: 'b', strategy: 's' }] + _zip: function (data, strategy) { + return $.map(data, function (value) { + return { value: value, strategy: strategy }; + }); + } + }); + + $.fn.textcomplete.Completer = Completer; +}(jQuery); + ++function ($) { + 'use strict'; + + var include = function (zippedData, datum) { + var i, elem; + var idProperty = datum.strategy.idProperty + for (i = 0; i < zippedData.length; i++) { + elem = zippedData[i]; + if (elem.strategy !== datum.strategy) continue; + if (idProperty) { + if (elem.value[idProperty] === datum.value[idProperty]) return true; + } else { + if (elem.value === datum.value) return true; + } + } + return false; + }; + + var dropdownViews = {}; + $(document).on('click', function (e) { + var id = e.originalEvent && e.originalEvent.keepTextCompleteDropdown; + $.each(dropdownViews, function (key, view) { + if (key !== id) { view.deactivate(); } + }); + }); + + // Dropdown view + // ============= + + // Construct Dropdown object. + // + // element - Textarea or contenteditable element. + function Dropdown(element, completer, option) { + this.$el = Dropdown.findOrCreateElement(option); + this.completer = completer; + this.id = completer.id + 'dropdown'; + this._data = []; // zipped data. + this.$inputEl = $(element); + this.option = option; + + // Override setPosition method. + if (option.listPosition) { this.setPosition = option.listPosition; } + if (option.height) { this.$el.height(option.height); } + var self = this; + $.each(['maxCount', 'placement', 'footer', 'header', 'className'], function (_i, name) { + if (option[name] != null) { self[name] = option[name]; } + }); + this._bindEvents(element); + dropdownViews[this.id] = this; + } + + $.extend(Dropdown, { + // Class methods + // ------------- + + findOrCreateElement: function (option) { + var $parent = option.appendTo; + if (!($parent instanceof $)) { $parent = $($parent); } + var $el = $parent.children('.dropdown-menu') + if (!$el.length) { + $el = $('').css({ + display: 'none', + left: 0, + position: 'absolute', + zIndex: option.zIndex + }).appendTo($parent); + } + return $el; + } + }); + + $.extend(Dropdown.prototype, { + // Public properties + // ----------------- + + $el: null, // jQuery object of ul.dropdown-menu element. + $inputEl: null, // jQuery object of target textarea. + completer: null, + footer: null, + header: null, + id: null, + maxCount: 10, + placement: '', + shown: false, + data: [], // Shown zipped data. + className: '', + + // Public methods + // -------------- + + destroy: function () { + // Don't remove $el because it may be shared by several textcompletes. + this.deactivate(); + + this.$el.off('.' + this.id); + this.$inputEl.off('.' + this.id); + this.clear(); + this.$el = this.$inputEl = this.completer = null; + delete dropdownViews[this.id] + }, + + render: function (zippedData) { + var contentsHtml = this._buildContents(zippedData); + var unzippedData = $.map(this.data, function (d) { return d.value; }); + if (this.data.length) { + this._renderHeader(unzippedData); + this._renderFooter(unzippedData); + if (contentsHtml) { + this._renderContents(contentsHtml); + this._activateIndexedItem(); + } + this._setScroll(); + } else if (this.shown) { + this.deactivate(); + } + }, + + setPosition: function (position) { + this.$el.css(this._applyPlacement(position)); + + // Make the dropdown fixed if the input is also fixed + // This can't be done during init, as textcomplete may be used on multiple elements on the same page + // Because the same dropdown is reused behind the scenes, we need to recheck every time the dropdown is showed + var position = 'absolute'; + // Check if input or one of its parents has positioning we need to care about + this.$inputEl.add(this.$inputEl.parents()).each(function() { + if($(this).css('position') === 'absolute') // The element has absolute positioning, so it's all OK + return false; + if($(this).css('position') === 'fixed') { + position = 'fixed'; + return false; + } + }); + this.$el.css({ position: position }); // Update positioning + + return this; + }, + + clear: function () { + this.$el.html(''); + this.data = []; + this._index = 0; + this._$header = this._$footer = null; + }, + + activate: function () { + if (!this.shown) { + this.clear(); + this.$el.show(); + if (this.className) { this.$el.addClass(this.className); } + this.completer.fire('textComplete:show'); + this.shown = true; + } + return this; + }, + + deactivate: function () { + if (this.shown) { + this.$el.hide(); + if (this.className) { this.$el.removeClass(this.className); } + this.completer.fire('textComplete:hide'); + this.shown = false; + } + return this; + }, + + isUp: function (e) { + return e.keyCode === 38 || (e.ctrlKey && e.keyCode === 80); // UP, Ctrl-P + }, + + isDown: function (e) { + return e.keyCode === 40 || (e.ctrlKey && e.keyCode === 78); // DOWN, Ctrl-N + }, + + isEnter: function (e) { + var modifiers = e.ctrlKey || e.altKey || e.metaKey || e.shiftKey; + return !modifiers && (e.keyCode === 13 || e.keyCode === 9 || (this.option.completeOnSpace === true && e.keyCode === 32)) // ENTER, TAB + }, + + isPageup: function (e) { + return e.keyCode === 33; // PAGEUP + }, + + isPagedown: function (e) { + return e.keyCode === 34; // PAGEDOWN + }, + + // Private properties + // ------------------ + + _data: null, // Currently shown zipped data. + _index: null, + _$header: null, + _$footer: null, + + // Private methods + // --------------- + + _bindEvents: function () { + this.$el.on('mousedown.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)) + this.$el.on('mouseover.' + this.id, '.textcomplete-item', $.proxy(this._onMouseover, this)); + this.$inputEl.on('keydown.' + this.id, $.proxy(this._onKeydown, this)); + }, + + _onClick: function (e) { + var $el = $(e.target); + e.preventDefault(); + e.originalEvent.keepTextCompleteDropdown = this.id; + if (!$el.hasClass('textcomplete-item')) { + $el = $el.closest('.textcomplete-item'); + } + var datum = this.data[parseInt($el.data('index'), 10)]; + this.completer.select(datum.value, datum.strategy); + var self = this; + // Deactive at next tick to allow other event handlers to know whether + // the dropdown has been shown or not. + setTimeout(function () { self.deactivate(); }, 0); + }, + + // Activate hovered item. + _onMouseover: function (e) { + var $el = $(e.target); + e.preventDefault(); + if (!$el.hasClass('textcomplete-item')) { + $el = $el.closest('.textcomplete-item'); + } + this._index = parseInt($el.data('index'), 10); + this._activateIndexedItem(); + }, + + _onKeydown: function (e) { + if (!this.shown) { return; } + if (this.isUp(e)) { + e.preventDefault(); + this._up(); + } else if (this.isDown(e)) { + e.preventDefault(); + this._down(); + } else if (this.isEnter(e)) { + e.preventDefault(); + this._enter(); + } else if (this.isPageup(e)) { + e.preventDefault(); + this._pageup(); + } else if (this.isPagedown(e)) { + e.preventDefault(); + this._pagedown(); + } + }, + + _up: function () { + if (this._index === 0) { + this._index = this.data.length - 1; + } else { + this._index -= 1; + } + this._activateIndexedItem(); + this._setScroll(); + }, + + _down: function () { + if (this._index === this.data.length - 1) { + this._index = 0; + } else { + this._index += 1; + } + this._activateIndexedItem(); + this._setScroll(); + }, + + _enter: function () { + var datum = this.data[parseInt(this._getActiveElement().data('index'), 10)]; + this.completer.select(datum.value, datum.strategy); + this._setScroll(); + }, + + _pageup: function () { + var target = 0; + var threshold = this._getActiveElement().position().top - this.$el.innerHeight(); + this.$el.children().each(function (i) { + if ($(this).position().top + $(this).outerHeight() > threshold) { + target = i; + return false; + } + }); + this._index = target; + this._activateIndexedItem(); + this._setScroll(); + }, + + _pagedown: function () { + var target = this.data.length - 1; + var threshold = this._getActiveElement().position().top + this.$el.innerHeight(); + this.$el.children().each(function (i) { + if ($(this).position().top > threshold) { + target = i; + return false + } + }); + this._index = target; + this._activateIndexedItem(); + this._setScroll(); + }, + + _activateIndexedItem: function () { + this.$el.find('.textcomplete-item.active').removeClass('active'); + this._getActiveElement().addClass('active'); + }, + + _getActiveElement: function () { + return this.$el.children('.textcomplete-item:nth(' + this._index + ')'); + }, + + _setScroll: function () { + var $activeEl = this._getActiveElement(); + var itemTop = $activeEl.position().top; + var itemHeight = $activeEl.outerHeight(); + var visibleHeight = this.$el.innerHeight(); + var visibleTop = this.$el.scrollTop(); + if (this._index === 0 || this._index == this.data.length - 1 || itemTop < 0) { + this.$el.scrollTop(itemTop + visibleTop); + } else if (itemTop + itemHeight > visibleHeight) { + this.$el.scrollTop(itemTop + itemHeight + visibleTop - visibleHeight); + } + }, + + _buildContents: function (zippedData) { + var datum, i, index; + var html = ''; + for (i = 0; i < zippedData.length; i++) { + if (this.data.length === this.maxCount) break; + datum = zippedData[i]; + if (include(this.data, datum)) { continue; } + index = this.data.length; + this.data.push(datum); + html += '
  • '; + html += datum.strategy.template(datum.value); + html += '
  • '; + } + return html; + }, + + _renderHeader: function (unzippedData) { + if (this.header) { + if (!this._$header) { + this._$header = $('
  • ').prependTo(this.$el); + } + var html = $.isFunction(this.header) ? this.header(unzippedData) : this.header; + this._$header.html(html); + } + }, + + _renderFooter: function (unzippedData) { + if (this.footer) { + if (!this._$footer) { + this._$footer = $('').appendTo(this.$el); + } + var html = $.isFunction(this.footer) ? this.footer(unzippedData) : this.footer; + this._$footer.html(html); + } + }, + + _renderContents: function (html) { + if (this._$footer) { + this._$footer.before(html); + } else { + this.$el.append(html); + } + }, + + _applyPlacement: function (position) { + // If the 'placement' option set to 'top', move the position above the element. + if (this.placement.indexOf('top') !== -1) { + // Overwrite the position object to set the 'bottom' property instead of the top. + position = { + top: 'auto', + bottom: this.$el.parent().height() - position.top + position.lineHeight, + left: position.left + }; + } else { + position.bottom = 'auto'; + delete position.lineHeight; + } + if (this.placement.indexOf('absleft') !== -1) { + position.left = 0; + } else if (this.placement.indexOf('absright') !== -1) { + position.right = 0; + position.left = 'auto'; + } + return position; + } + }); + + $.fn.textcomplete.Dropdown = Dropdown; +}(jQuery); + ++function ($) { + 'use strict'; + + // Memoize a search function. + var memoize = function (func) { + var memo = {}; + return function (term, callback) { + if (memo[term]) { + callback(memo[term]); + } else { + func.call(this, term, function (data) { + memo[term] = (memo[term] || []).concat(data); + callback.apply(null, arguments); + }); + } + }; + }; + + function Strategy(options) { + $.extend(this, options); + if (this.cache) { this.search = memoize(this.search); } + } + + Strategy.parse = function (optionsArray) { + return $.map(optionsArray, function (options) { + return new Strategy(options); + }); + }; + + $.extend(Strategy.prototype, { + // Public properties + // ----------------- + + // Required + match: null, + replace: null, + search: null, + + // Optional + cache: false, + context: function () { return true; }, + index: 2, + template: function (obj) { return obj; }, + idProperty: null + }); + + $.fn.textcomplete.Strategy = Strategy; + +}(jQuery); + ++function ($) { + 'use strict'; + + var now = Date.now || function () { return new Date().getTime(); }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // `wait` msec. + // + // This utility function was originally implemented at Underscore.js. + var debounce = function (func, wait) { + var timeout, args, context, timestamp, result; + var later = function () { + var last = now() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + result = func.apply(context, args); + context = args = null; + } + }; + + return function () { + context = this; + args = arguments; + timestamp = now(); + if (!timeout) { + timeout = setTimeout(later, wait); + } + return result; + }; + }; + + function Adapter () {} + + $.extend(Adapter.prototype, { + // Public properties + // ----------------- + + id: null, // Identity. + completer: null, // Completer object which creates it. + el: null, // Textarea element. + $el: null, // jQuery object of the textarea. + option: null, + + // Public methods + // -------------- + + initialize: function (element, completer, option) { + this.el = element; + this.$el = $(element); + this.id = completer.id + this.constructor.name; + this.completer = completer; + this.option = option; + + if (this.option.debounce) { + this._onKeyup = debounce(this._onKeyup, this.option.debounce); + } + + this._bindEvents(); + }, + + destroy: function () { + this.$el.off('.' + this.id); // Remove all event handlers. + this.$el = this.el = this.completer = null; + }, + + // Update the element with the given value and strategy. + // + // value - The selected object. It is one of the item of the array + // which was callbacked from the search function. + // strategy - The Strategy associated with the selected value. + select: function (/* value, strategy */) { + throw new Error('Not implemented'); + }, + + // Returns the caret's relative coordinates from body's left top corner. + // + // FIXME: Calculate the left top corner of `this.option.appendTo` element. + getCaretPosition: function () { + var position = this._getCaretRelativePosition(); + var offset = this.$el.offset(); + position.top += offset.top; + position.left += offset.left; + return position; + }, + + // Focus on the element. + focus: function () { + this.$el.focus(); + }, + + // Private methods + // --------------- + + _bindEvents: function () { + this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this)); + }, + + _onKeyup: function (e) { + if (this._skipSearch(e)) { return; } + this.completer.trigger(this.getTextFromHeadToCaret(), true); + }, + + // Suppress searching if it returns true. + _skipSearch: function (clickEvent) { + switch (clickEvent.keyCode) { + case 40: // DOWN + case 38: // UP + return true; + } + if (clickEvent.ctrlKey) switch (clickEvent.keyCode) { + case 78: // Ctrl-N + case 80: // Ctrl-P + return true; + } + } + }); + + $.fn.textcomplete.Adapter = Adapter; +}(jQuery); + ++function ($) { + 'use strict'; + + // Textarea adapter + // ================ + // + // Managing a textarea. It doesn't know a Dropdown. + function Textarea(element, completer, option) { + this.initialize(element, completer, option); + } + + Textarea.DIV_PROPERTIES = { + left: -9999, + position: 'absolute', + top: 0, + whiteSpace: 'pre-wrap' + } + + Textarea.COPY_PROPERTIES = [ + 'border-width', 'font-family', 'font-size', 'font-style', 'font-variant', + 'font-weight', 'height', 'letter-spacing', 'word-spacing', 'line-height', + 'text-decoration', 'text-align', 'width', 'padding-top', 'padding-right', + 'padding-bottom', 'padding-left', 'margin-top', 'margin-right', + 'margin-bottom', 'margin-left', 'border-style', 'box-sizing', 'tab-size' + ]; + + $.extend(Textarea.prototype, $.fn.textcomplete.Adapter.prototype, { + // Public methods + // -------------- + + // Update the textarea with the given value and strategy. + select: function (value, strategy) { + var pre = this.getTextFromHeadToCaret(); + var post = this.el.value.substring(this.el.selectionEnd); + var newSubstr = strategy.replace(value); + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr); + this.$el.val(pre + post); + this.el.selectionStart = this.el.selectionEnd = pre.length; + }, + + // Private methods + // --------------- + + // Returns the caret's relative coordinates from textarea's left top corner. + // + // Browser native API does not provide the way to know the position of + // caret in pixels, so that here we use a kind of hack to accomplish + // the aim. First of all it puts a dummy div element and completely copies + // the textarea's style to the element, then it inserts the text and a + // span element into the textarea. + // Consequently, the span element's position is the thing what we want. + _getCaretRelativePosition: function () { + var dummyDiv = $('
    ').css(this._copyCss()) + .text(this.getTextFromHeadToCaret()); + var span = $('').text('.').appendTo(dummyDiv); + this.$el.before(dummyDiv); + var position = span.position(); + position.top += span.height() - this.$el.scrollTop(); + position.lineHeight = span.height(); + dummyDiv.remove(); + return position; + }, + + _copyCss: function () { + return $.extend({ + // Set 'scroll' if a scrollbar is being shown; otherwise 'auto'. + overflow: this.el.scrollHeight > this.el.offsetHeight ? 'scroll' : 'auto' + }, Textarea.DIV_PROPERTIES, this._getStyles()); + }, + + _getStyles: (function ($) { + var color = $('
    ').css(['color']).color; + if (typeof color !== 'undefined') { + return function () { + return this.$el.css(Textarea.COPY_PROPERTIES); + }; + } else { // jQuery < 1.8 + return function () { + var $el = this.$el; + var styles = {}; + $.each(Textarea.COPY_PROPERTIES, function (i, property) { + styles[property] = $el.css(property); + }); + return styles; + }; + } + })($), + + getTextFromHeadToCaret: function () { + return this.el.value.substring(0, this.el.selectionEnd); + } + }); + + $.fn.textcomplete.Textarea = Textarea; +}(jQuery); + ++function ($) { + 'use strict'; + + var sentinelChar = '吶'; + + function IETextarea(element, completer, option) { + this.initialize(element, completer, option); + $('' + sentinelChar + '').css({ + position: 'absolute', + top: -9999, + left: -9999 + }).insertBefore(element); + } + + $.extend(IETextarea.prototype, $.fn.textcomplete.Textarea.prototype, { + // Public methods + // -------------- + + select: function (value, strategy) { + var pre = this.getTextFromHeadToCaret(); + var post = this.el.value.substring(pre.length); + var newSubstr = strategy.replace(value); + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr); + this.$el.val(pre + post); + this.el.focus(); + var range = this.el.createTextRange(); + range.collapse(true); + range.moveEnd('character', pre.length); + range.moveStart('character', pre.length); + range.select(); + }, + + getTextFromHeadToCaret: function () { + this.el.focus(); + var range = document.selection.createRange(); + range.moveStart('character', -this.el.value.length); + var arr = range.text.split(sentinelChar) + return arr.length === 1 ? arr[0] : arr[1]; + } + }); + + $.fn.textcomplete.IETextarea = IETextarea; +}(jQuery); + +// NOTE: TextComplete plugin has contenteditable support but it does not work +// fine especially on old IEs. +// Any pull requests are REALLY welcome. + ++function ($) { + 'use strict'; + + // ContentEditable adapter + // ======================= + // + // Adapter for contenteditable elements. + function ContentEditable (element, completer, option) { + this.initialize(element, completer, option); + } + + $.extend(ContentEditable.prototype, $.fn.textcomplete.Adapter.prototype, { + // Public methods + // -------------- + + // Update the content with the given value and strategy. + // When an dropdown item is selected, it is executed. + select: function (value, strategy) { + var pre = this.getTextFromHeadToCaret(); + var sel = window.getSelection() + var range = sel.getRangeAt(0); + var selection = range.cloneRange(); + selection.selectNodeContents(range.startContainer); + var content = selection.toString(); + var post = content.substring(range.startOffset); + var newSubstr = strategy.replace(value); + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr); + range.selectNodeContents(range.startContainer); + range.deleteContents(); + var node = document.createTextNode(pre + post); + range.insertNode(node); + range.setStart(node, pre.length); + range.collapse(true); + sel.removeAllRanges(); + sel.addRange(range); + }, + + // Private methods + // --------------- + + // Returns the caret's relative position from the contenteditable's + // left top corner. + // + // Examples + // + // this._getCaretRelativePosition() + // //=> { top: 18, left: 200, lineHeight: 16 } + // + // Dropdown's position will be decided using the result. + _getCaretRelativePosition: function () { + var range = window.getSelection().getRangeAt(0).cloneRange(); + var node = document.createElement('span'); + range.insertNode(node); + range.selectNodeContents(node); + range.deleteContents(); + var $node = $(node); + var position = $node.offset(); + position.left -= this.$el.offset().left; + position.top += $node.height() - this.$el.offset().top; + position.lineHeight = $node.height(); + var dir = this.$el.attr('dir') || this.$el.css('direction'); + if (dir === 'rtl') { position.left -= this.listView.$el.width(); } + return position; + }, + + // Returns the string between the first character and the caret. + // Completer will be triggered with the result for start autocompleting. + // + // Example + // + // // Suppose the html is 'hello wor|ld' and | is the caret. + // this.getTextFromHeadToCaret() + // // => ' wor' // not 'hello wor' + getTextFromHeadToCaret: function () { + var range = window.getSelection().getRangeAt(0); + var selection = range.cloneRange(); + selection.selectNodeContents(range.startContainer); + return selection.toString().substring(0, range.startOffset); + } + }); + + $.fn.textcomplete.ContentEditable = ContentEditable; +}(jQuery); diff --git a/view/templates/display-head.tpl b/view/templates/display-head.tpl index 2050cdec0..9a96a2398 100644 --- a/view/templates/display-head.tpl +++ b/view/templates/display-head.tpl @@ -1,9 +1,9 @@ diff --git a/view/templates/head.tpl b/view/templates/head.tpl index 17c459c4d..129e5cfd2 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -33,7 +33,9 @@ - + + + diff --git a/view/templates/jot-header.tpl b/view/templates/jot-header.tpl index 647f261c4..84ce88a88 100644 --- a/view/templates/jot-header.tpl +++ b/view/templates/jot-header.tpl @@ -12,7 +12,7 @@ function initEditor(cb){ if(plaintext == 'none') { $("#profile-jot-text-loading").hide(); $("#profile-jot-text").css({ 'height': 200, 'color': '#000' }); - $("#profile-jot-text").contact_autocomplete(baseurl+"/acl"); + $("#profile-jot-text").editor_autocomplete(baseurl+"/acl"); editor = true; $("a#jot-perms-icon").colorbox({ 'inline' : true, From c42922f25f34f7d34fa50bc743680169542ac4a5 Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Wed, 20 Jan 2016 03:34:22 +0100 Subject: [PATCH 02/17] rework autocomplete: make remote and local search work --- include/acl_selectors.php | 75 +++++++++++++++++++++++++++++++++++++++ include/dir_fns.php | 36 +++++++++++++++++++ include/nav.php | 8 +++++ 3 files changed, 119 insertions(+) create mode 100644 include/dir_fns.php diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 69181b735..19197981e 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -545,6 +545,26 @@ function acl_lookup(&$a, $out_type = 'json') { intval(local_user()) ); } + elseif($type == 'x') { + $r = navbar_complete($a); + $contacts = array(); + if($r) { + foreach($r as $g) { + $contacts[] = array( + "photo" => $g['photo'], + "name" => $g['name'], + "nick" => (x($g['addr']) ? $g['addr'] : $g['url']), + ); + } + } + $o = array( + 'start' => $start, + 'count' => $count, + 'items' => $contacts, + ); + echo json_encode($o); + killme(); + } else $r = array(); @@ -655,3 +675,58 @@ function acl_lookup(&$a, $out_type = 'json') { killme(); } +function navbar_complete(&$a) { + +// logger('navbar_complete'); + + if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) { + return; + } + + $local = get_config('system','poco_local_search'); + $local = true; + + $search = $prefix.notags(trim($_REQUEST['search'])); + if(! $search || mb_strlen($search) < 2) + return array(); + + $star = false; + $address = false; + + if(substr($search,0,1) === '@') + $search = substr($search,1); + + if(substr($search,0,1) === '*') { + $star = true; + $search = substr($search,1); + } + + if(strpos($search,'@') !== false) { + $address = true; + } + + if($local) { + require_once("include/dir_fns.php"); + $x = dirsearch_autocomplete($search); + return $x; + } + + if(! $local) { + require_once("include/dir_fns.php"); + $url = $directory['url'] . '/dirsearch'; + + + $p = (($a->pager['page'] != 1) ? '&p=' . $a->pager['page'] : ''); + + + $x = z_fetch_url(get_server().'/lsearch?f=' . $p . '&search=' . urlencode($search)); + if($x['success']) { + $t = 0; + $j = json_decode($x['body'],true); + if($j && $j['results']) { + return $j['results']; + } + } + } + return; +} \ No newline at end of file diff --git a/include/dir_fns.php b/include/dir_fns.php new file mode 100644 index 000000000..a362732b7 --- /dev/null +++ b/include/dir_fns.php @@ -0,0 +1,36 @@ + 0 OR (NOT `gcontact`.`hide` AND `gcontact`.`network` IN ('%s', '%s', '%s') AND + ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)))) AND + (`gcontact`.`url` REGEXP '%s' OR `gcontact`.`name` REGEXP '%s' OR `gcontact`.`nick` REGEXP '%s' + ) + GROUP BY `gcontact`.`nurl` + ORDER BY `gcontact`.`updated` DESC ", + intval(local_user()), dbesc(CONTACT_IS_SHARING), dbesc(CONTACT_IS_FRIEND), + dbesc(NETWORK_DFRN), dbesc($ostatus), dbesc($diaspora), + dbesc(escape_tags($search)), dbesc(escape_tags($search)), dbesc(escape_tags($search))); + return $results; + } + +} \ No newline at end of file diff --git a/include/nav.php b/include/nav.php index 6512d3560..0261ab7d6 100644 --- a/include/nav.php +++ b/include/nav.php @@ -11,10 +11,18 @@ function nav(&$a) { if(!(x($a->page,'nav'))) $a->page['nav'] = ''; + $base = z_root(); /** * Placeholder div for popup panel */ + $a->page['htmlhead'] .= <<< EOT + + +EOT; $a->page['nav'] .= '' ; $nav_info = nav_info($a); From e3475bd5876b3a5b60506840738deec232e64d57 Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Thu, 21 Jan 2016 13:28:29 +0100 Subject: [PATCH 03/17] rework autocomplete: css work --- js/autocomplete.js | 171 ++++++++++++++++++- view/global.css | 57 ++++++- view/templates/msg-header.tpl | 2 +- view/templates/wallmsg-header.tpl | 2 +- view/theme/duepuntozero/style.css | 16 +- view/theme/frost-mobile/js/main.js | 2 +- view/theme/frost-mobile/js/theme.js | 4 +- view/theme/frost-mobile/style.css | 58 +++++++ view/theme/frost-mobile/templates/end.tpl | 3 +- view/theme/frost/js/main.js | 2 +- view/theme/frost/js/theme.js | 6 +- view/theme/frost/style.css | 65 ++++++- view/theme/frost/templates/end.tpl | 3 +- view/theme/quattro/dark/style.css | 17 +- view/theme/quattro/green/style.css | 17 +- view/theme/quattro/lilac/style.css | 23 ++- view/theme/quattro/quattro.less | 12 +- view/theme/smoothly/style.css | 13 +- view/theme/smoothly/templates/jot-header.tpl | 2 +- view/theme/vier/style.css | 21 ++- 20 files changed, 460 insertions(+), 36 deletions(-) diff --git a/js/autocomplete.js b/js/autocomplete.js index aa4494b71..1f7df011d 100644 --- a/js/autocomplete.js +++ b/js/autocomplete.js @@ -1,10 +1,14 @@ /** - * Red people autocomplete + * Friendica people autocomplete * * require jQuery, jquery.textcomplete */ function contact_search(term, callback, backend_url, type) { + // Check if there is a conversation id to include the unkonwn contacts of the conversation + var conv_id = document.activeElement.id.match(/\d+$/); + + // 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] = {}; @@ -27,6 +31,9 @@ function contact_search(term, callback, backend_url, type) { type:type, }; + if(conv_id !== null) + postdata['conversation'] = conv_id[0]; + $.ajax({ type:'POST', @@ -54,7 +61,7 @@ function contact_format(item) { var desc = ((item.label) ? item.nick + ' ' + item.label : item.nick); if(typeof desc === 'undefined') desc = ''; if(desc) desc = ' ('+desc+')'; - return "
    {2}{3}
    ".format(item.taggable, item.photo, item.name, desc, item.link); + return "
    {2}{3}
    ".format(item.taggable, item.photo, item.name, desc, item.link); } else return "
    " + item.text + "
    "; @@ -190,3 +197,163 @@ function submit_form(e) { a.on('textComplete:select', function(e, value, strategy) { onselect(value); }); }; })( jQuery ); + + +/** + * 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'; + /* style['max-height'] = '150px'; + style.border = '1px solid red'; + style.background = '#cccccc'; + + style.overflow = 'auto'; + style['z-index'] = '100000'; + */ + style.display = 'none'; + + this.cont = $("
    "); + 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 = "{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 = $("
    "+label+"
    "); + 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(); + } +} + diff --git a/view/global.css b/view/global.css index 8646bf8e4..ff2810292 100644 --- a/view/global.css +++ b/view/global.css @@ -177,12 +177,67 @@ key { display: inline; background-color: #eee; color: #666; padding:0.2em; font- /* fields help text */ .field .field_help { - clear: left; + clear: left; } /* notifications unseen */ .notify-unseen { background-color: #cceeFF; } +/* autocomplete popup */ + +ul.acpopup { + list-style: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; +} +nav .acpopup { + width: 290px; + margin-left: -35px; + max-height: 450px; + max-width: 300px; + overflow-y: auto; + overflow-x: hidden; + margin-top: 0px; +} +img.acpopup-img { + float: left; + width: 36px; + height: 36px; + margin-right: 5px; + vertical-align: middle; +} +.acpopup-contactname { + padding-top: 2px; + font-weight: bold; + line-height: 1em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; +} +.acpopup-sub-text { + color: #777; + font-size: 0.833em; + line-height: 1em; + overflow: hidden; + text-overflow: ellipsis; + display: block; +} +.textcomplete-item a { + color: inherit; + overflow: hidden; + text-overflow: ellipsis; + clear: both; + white-space: nowrap; + padding: 3px 20px; + display: block; +} +.textcomplete-item a:hover { + text-decoration: none; +} + /* plain text editor upload/select popup */ .fbrowser .path a { padding: 5px; } diff --git a/view/templates/msg-header.tpl b/view/templates/msg-header.tpl index 86598bbf6..9b1a92ef5 100644 --- a/view/templates/msg-header.tpl +++ b/view/templates/msg-header.tpl @@ -44,7 +44,7 @@ if(plaintext != 'none') { }); } else - $("#prvmail-text").contact_autocomplete(baseurl+"/acl"); + $("#prvmail-text").editor_autocomplete(baseurl+"/acl"); diff --git a/view/templates/wallmsg-header.tpl b/view/templates/wallmsg-header.tpl index f03f496fe..2d4cd2379 100644 --- a/view/templates/wallmsg-header.tpl +++ b/view/templates/wallmsg-header.tpl @@ -44,7 +44,7 @@ if(plaintext != 'none') { }); } else - $("#prvmail-text").contact_autocomplete(baseurl+"/acl"); + $("#prvmail-text").editor_autocomplete(baseurl+"/acl"); diff --git a/view/theme/duepuntozero/style.css b/view/theme/duepuntozero/style.css index c004eb53d..e5cc39046 100644 --- a/view/theme/duepuntozero/style.css +++ b/view/theme/duepuntozero/style.css @@ -144,7 +144,7 @@ nav #banner #logo-text a:hover { text-decoration: none; } height: 15px; margin-top: 67px; margin-right: 2px; - //padding: 6px 10px; + /*padding: 6px 10px;*/ padding: 6px 3px; float: left; bottom: 140px; @@ -244,7 +244,7 @@ section { display:block; float:left; padding: 0.4em; - //margin-right: 1em; + /*margin-right: 1em;*/ margin-right: 3px ; } .tab.active { @@ -3288,6 +3288,12 @@ aside input[type='text'] { /* autocomplete popup */ .acpopup { + background-color:#ffffff; + overflow:auto; + z-index:100000; + border:1px solid #cccccc; +} +.acpopup-mce { max-height:150px; background-color:#ffffff; overflow:auto; @@ -3306,6 +3312,12 @@ aside input[type='text'] { .acpopupitem.selected { color: #FFFFFF; background: #3465A4; } +.textcomplete-item.active { + color: #FFFFFF; background: #3465A4; +} +.active a .acpopup-sub-text { + color: #fff; +} /* popup notifications */ div.jGrowl div.notice { diff --git a/view/theme/frost-mobile/js/main.js b/view/theme/frost-mobile/js/main.js index 9eac71be8..d24e41e8c 100644 --- a/view/theme/frost-mobile/js/main.js +++ b/view/theme/frost-mobile/js/main.js @@ -389,7 +389,7 @@ $('body').css('cursor', 'auto'); } /* autocomplete @nicknames */ - $(".comment-edit-form textarea").contact_autocomplete(baseurl+"/acl"); + $(".comment-edit-form textarea").editor_autocomplete(baseurl+"/acl"); // setup videos, since VideoJS won't take care of any loaded via AJAX if(typeof videojs != 'undefined') videojs.autoSetup(); diff --git a/view/theme/frost-mobile/js/theme.js b/view/theme/frost-mobile/js/theme.js index 8133c602c..c02137087 100644 --- a/view/theme/frost-mobile/js/theme.js +++ b/view/theme/frost-mobile/js/theme.js @@ -121,7 +121,7 @@ $(document).ready(function() { a.setOptions({ params: { type: 'a' }}); break; case 'display-head': - $(".comment-wwedit-wrapper textarea").contact_autocomplete(baseurl+"/acl"); + $(".comment-wwedit-wrapper textarea").editor_autocomplete(baseurl+"/acl"); break; default: break; @@ -286,7 +286,7 @@ function initEditor(cb){ if(plaintext == 'none') { // $("#profile-jot-text-loading").hide(); $("#profile-jot-text").css({ 'height': 200, 'color': '#000' }); - $("#profile-jot-text").contact_autocomplete(baseurl+"/acl"); + $("#profile-jot-text").editor_autocomplete(baseurl+"/acl"); editor = true; /* $("a#jot-perms-icon").colorbox({ 'inline' : true, diff --git a/view/theme/frost-mobile/style.css b/view/theme/frost-mobile/style.css index 400b23c10..24373eda5 100644 --- a/view/theme/frost-mobile/style.css +++ b/view/theme/frost-mobile/style.css @@ -4160,6 +4160,64 @@ aside input[type='text'] { .acpopupitem.selected { color: #FFFFFF; background: #3465A4; } +ul.acpopup { + list-style: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; +} +nav .acpopup { + width: 290px; + margin-left: -35px; + max-height: 450px; + max-width: 300px; + overflow-y: auto; + overflow-x: hidden; + margin-top: 0px; +} +img.acpopup-img { + float: left; + width: 36px; + height: 36px; + margin-right: 5px; + vertical-align: middle; +} +.acpopup-contactname { + padding-top: 2px; + font-weight: bold; + line-height: 1em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; +} +.acpopup-sub-text { + color: #777; + font-size: 0.833em; + line-height: 1em; + overflow: hidden; + text-overflow: ellipsis; + display: block; +} +.textcomplete-item a { + color: inherit; + overflow: hidden; + text-overflow: ellipsis; + clear: both; + white-space: nowrap; + padding: 3px 20px; + display: block; +} +.textcomplete-item a:hover { + text-decoration: none; +} +.textcomplete-item.active { + color: #FFFFFF; background: #3465A4; +} +.active a .acpopup-sub-text { + color: #fff; +} /* popup notifications */ div.jGrowl div.notice { diff --git a/view/theme/frost-mobile/templates/end.tpl b/view/theme/frost-mobile/templates/end.tpl index 8d4b26bdc..c1acbfb2a 100644 --- a/view/theme/frost-mobile/templates/end.tpl +++ b/view/theme/frost-mobile/templates/end.tpl @@ -7,7 +7,8 @@ - + + diff --git a/view/theme/frost/js/main.js b/view/theme/frost/js/main.js index 5483ad6bc..733064b30 100644 --- a/view/theme/frost/js/main.js +++ b/view/theme/frost/js/main.js @@ -402,7 +402,7 @@ $('body').css('cursor', 'auto'); } /* autocomplete @nicknames */ - $(".comment-edit-form textarea").contact_autocomplete(baseurl+"/acl"); + $(".comment-edit-form textarea").editor_autocomplete(baseurl+"/acl"); collapseHeight(); diff --git a/view/theme/frost/js/theme.js b/view/theme/frost/js/theme.js index a14a034bc..4418c1f3c 100644 --- a/view/theme/frost/js/theme.js +++ b/view/theme/frost/js/theme.js @@ -232,7 +232,7 @@ $(document).ready(function() { a.setOptions({ params: { type: 'a' }}); break; case 'display-head': - $(".comment-wwedit-wrapper textarea").contact_autocomplete(baseurl+"/acl"); + $(".comment-wwedit-wrapper textarea").editor_autocomplete(baseurl+"/acl"); break; default: break; @@ -587,7 +587,7 @@ function initEditor(cb){ plaintextFn : function() { $("#profile-jot-text-loading").hide(); $("#profile-jot-text").css({ 'height': 200, 'color': '#000' }); - $("#profile-jot-text").contact_autocomplete(baseurl+"/acl"); + $("#profile-jot-text").editor_autocomplete(baseurl+"/acl"); $(".jothidden").show(); if (typeof cb!="undefined") cb(); } @@ -660,7 +660,7 @@ function msgInitEditor() { }); }, plaintextFn : function() { - $("#prvmail-text").contact_autocomplete(baseurl+"/acl"); + $("#prvmail-text").editor_autocomplete(baseurl+"/acl"); } } InitMCEEditor(editorData); diff --git a/view/theme/frost/style.css b/view/theme/frost/style.css index 3dd400c76..fe07ad265 100644 --- a/view/theme/frost/style.css +++ b/view/theme/frost/style.css @@ -4010,13 +4010,15 @@ aside input[type='text'] { /* autocomplete popup */ -.acpopup { - max-height:150px; +.acpopup, .acpopup-mce { background-color:#ffffff; overflow:auto; z-index:100000; border:1px solid #cccccc; } +.acpopup-mce { + max-height:150px; +} .acpopupitem { background-color:#ffffff; padding: 4px; clear:left; @@ -4030,6 +4032,65 @@ aside input[type='text'] { color: #FFFFFF; background: #3465A4; } +ul.acpopup { + list-style: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; +} +nav .acpopup { + width: 290px; + margin-left: -35px; + max-height: 450px; + max-width: 300px; + overflow-y: auto; + overflow-x: hidden; + margin-top: 0px; +} +img.acpopup-img { + float: left; + width: 36px; + height: 36px; + margin-right: 5px; + vertical-align: middle; +} +.acpopup-contactname { + padding-top: 2px; + font-weight: bold; + line-height: 1em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; +} +.acpopup-sub-text { + color: #777; + font-size: 0.833em; + line-height: 1em; + overflow: hidden; + text-overflow: ellipsis; + display: block; +} +.textcomplete-item a { + color: inherit; + overflow: hidden; + text-overflow: ellipsis; + clear: both; + white-space: nowrap; + padding: 3px 20px; + display: block; +} +.textcomplete-item a:hover { + text-decoration: none; +} +.textcomplete-item.active { + color: #FFFFFF; background: #3465A4; +} +.active a .acpopup-sub-text { + color: #fff; +} + /* popup notifications */ div.jGrowl div.notice { background: #511919 url("../../../images/icons/48/notice.png") no-repeat 5px center; diff --git a/view/theme/frost/templates/end.tpl b/view/theme/frost/templates/end.tpl index 4242f80c8..e864a9498 100644 --- a/view/theme/frost/templates/end.tpl +++ b/view/theme/frost/templates/end.tpl @@ -19,7 +19,8 @@ - + + diff --git a/view/theme/quattro/dark/style.css b/view/theme/quattro/dark/style.css index 847017ee5..04e7a2ad5 100644 --- a/view/theme/quattro/dark/style.css +++ b/view/theme/quattro/dark/style.css @@ -463,7 +463,7 @@ a:hover { text-decoration: underline; } blockquote { - background: #FFFFFF; + background: #ffffff; padding: 1em; margin-left: 1em; border-left: 1em solid #e6e6e6; @@ -744,8 +744,12 @@ ul.menu-popup .toolbar a:hover { } /* autocomplete popup */ .autocomplete, -.acpopup { +.acpopup-mce { max-height: 150px; +} +.autocomplete, +.acpopup-mce, +.acpopup { background-color: #ffffff; color: #2d2d2d; border: 1px solid #364e59; @@ -774,6 +778,15 @@ ul.menu-popup .toolbar a:hover { .acpopupitem.selected { background-color: #bdcdd4; } +.textcomplete-item { + color: #2d2d2d; +} +.textcomplete-item a:hover { + color: #2d2d2d; +} +.textcomplete-item.active { + background-color: #bdcdd4; +} #nav-notifications-menu { width: 400px; max-height: 550px; diff --git a/view/theme/quattro/green/style.css b/view/theme/quattro/green/style.css index 4cfcb5927..13f328d02 100644 --- a/view/theme/quattro/green/style.css +++ b/view/theme/quattro/green/style.css @@ -463,7 +463,7 @@ a:hover { text-decoration: underline; } blockquote { - background: #FFFFFF; + background: #ffffff; padding: 1em; margin-left: 1em; border-left: 1em solid #e6e6e6; @@ -744,8 +744,12 @@ ul.menu-popup .toolbar a:hover { } /* autocomplete popup */ .autocomplete, -.acpopup { +.acpopup-mce { max-height: 150px; +} +.autocomplete, +.acpopup-mce, +.acpopup { background-color: #ffffff; color: #2d2d2d; border: 1px solid #364e59; @@ -774,6 +778,15 @@ ul.menu-popup .toolbar a:hover { .acpopupitem.selected { background-color: #ccff42; } +.textcomplete-item { + color: #2d2d2d; +} +.textcomplete-item a:hover { + color: #2d2d2d; +} +.textcomplete-item.active { + background-color: #ccff42; +} #nav-notifications-menu { width: 400px; max-height: 550px; diff --git a/view/theme/quattro/lilac/style.css b/view/theme/quattro/lilac/style.css index 2ff7cfcb0..5ad7e2028 100644 --- a/view/theme/quattro/lilac/style.css +++ b/view/theme/quattro/lilac/style.css @@ -420,7 +420,7 @@ body { font-family: Liberation Sans, helvetica, arial, clean, sans-serif; font-size: 11px; - background-color: #F6ECF9; + background-color: #f6ecf9; color: #2d2d2d; margin: 50px 0 0 0; display: table; @@ -463,7 +463,7 @@ a:hover { text-decoration: underline; } blockquote { - background: #FFFFFF; + background: #ffffff; padding: 1em; margin-left: 1em; border-left: 1em solid #e6e6e6; @@ -744,8 +744,12 @@ ul.menu-popup .toolbar a:hover { } /* autocomplete popup */ .autocomplete, -.acpopup { +.acpopup-mce { max-height: 150px; +} +.autocomplete, +.acpopup-mce, +.acpopup { background-color: #ffffff; color: #2d2d2d; border: 1px solid #364e59; @@ -774,6 +778,15 @@ ul.menu-popup .toolbar a:hover { .acpopupitem.selected { background-color: #c0a3c7; } +.textcomplete-item { + color: #2d2d2d; +} +.textcomplete-item a:hover { + color: #2d2d2d; +} +.textcomplete-item.active { + background-color: #c0a3c7; +} #nav-notifications-menu { width: 400px; max-height: 550px; @@ -1753,7 +1766,7 @@ span[id^="showmore-wrap"] { height: 20px; width: 500px; font-weight: bold; - border: 1px solid #F6ECF9; + border: 1px solid #f6ecf9; } #jot #jot-title:-webkit-input-placeholder { font-weight: normal; @@ -1780,7 +1793,7 @@ span[id^="showmore-wrap"] { margin: 0; height: 20px; width: 200px; - border: 1px solid #F6ECF9; + border: 1px solid #f6ecf9; } #jot #jot-category:hover { border: 1px solid #999999; diff --git a/view/theme/quattro/quattro.less b/view/theme/quattro/quattro.less index 681cfcc37..3360ce337 100644 --- a/view/theme/quattro/quattro.less +++ b/view/theme/quattro/quattro.less @@ -265,9 +265,10 @@ ul.menu-popup { } /* autocomplete popup */ +.autocomplete, .acpopup-mce { max-height:150px; } .autocomplete, +.acpopup-mce, .acpopup { - max-height:150px; background-color:@MenuBg; color: @Menu; border:1px solid @MenuBorder; @@ -291,6 +292,15 @@ ul.menu-popup { background-color: @MenuItemHoverBg; } } +.textcomplete-item { + color: @MenuItem; + a:hover{ + color: @MenuItem; + } + &.active{ + background-color: @MenuItemHoverBg; + } +} #nav-notifications-menu { diff --git a/view/theme/smoothly/style.css b/view/theme/smoothly/style.css index b9f094932..141594960 100644 --- a/view/theme/smoothly/style.css +++ b/view/theme/smoothly/style.css @@ -4353,8 +4353,7 @@ ul.menu-popup { } /* autocomplete popup */ -.acpopup { - max-height: 150px; +.acpopup, acpopup-mce { overflow: auto; z-index: 100000; color: #2e3436; @@ -4371,6 +4370,10 @@ ul.menu-popup { -webkit-box-shadow: 0 0 8px #BDBDBD; } +.acpopup-mce { + max-height: 150px; +} + .acpopupitem { color: #2e3436; padding: 4px; @@ -4381,7 +4384,7 @@ ul.menu-popup { margin-right: 4px; } -.acpopupitem.selected { +.acpopupitem.selected, .textcomplete-item.active { color: #efefef; background: -webkit-gradient( linear, left top, left bottom, color-stop(0.05, #1873a2), color-stop(1, #6da6c4) ); background: -moz-linear-gradient( center top, #1873a2 5%, #6da6c4 100% ); @@ -4390,6 +4393,10 @@ ul.menu-popup { order-bottom: none; } +.textcomplete-item a:hover, .textcomplete-item a:hover .acpopup-sub-text, .textcomplete-item.active a .acpopup-sub-text { + color: #efefef; +} + .qcomment { opacity: 0.8; filter: alpha(opacity=0); diff --git a/view/theme/smoothly/templates/jot-header.tpl b/view/theme/smoothly/templates/jot-header.tpl index f096d2582..8b2666f0f 100644 --- a/view/theme/smoothly/templates/jot-header.tpl +++ b/view/theme/smoothly/templates/jot-header.tpl @@ -12,7 +12,7 @@ function initEditor(cb){ if(plaintext == 'none') { $("#profile-jot-text-loading").hide(); $("#profile-jot-text").css({ 'height': 200, 'color': '#000' }); - $("#profile-jot-text").contact_autocomplete(baseurl+"/acl"); + $("#profile-jot-text").editor_autocomplete(baseurl+"/acl"); $(".jothidden").show(); editor = true; $("a#jot-perms-icon").colorbox({ diff --git a/view/theme/vier/style.css b/view/theme/vier/style.css index defeec702..ddf3df1de 100644 --- a/view/theme/vier/style.css +++ b/view/theme/vier/style.css @@ -430,7 +430,7 @@ code { .sidebar-group-li:hover, #sidebar-new-group:hover, #sidebar-edit-groups:hover,#forum-widget-collapse:hover, #sidebar-ungrouped:hover, .side-link:hover, .nets-ul li:hover, #forumlist-sidebar li:hover, #forumlist-sidebar-right li:hover, .nets-all:hover, .saved-search-li:hover, li.tool:hover, .admin.link:hover, aside h4 a:hover, right_aside h4 a:hover, #message-new:hover, -#sidebar-photos-albums li:hover, .photos-upload-link:hover { +#sidebar-photos-albums li:hover, .photos-upload-link:hover, .textcomplete-item.active { /* background-color: #ddd; */ /* background-color: #e5e5e5; */ background-color: #F5F5F5; @@ -900,15 +900,18 @@ ul.menu-popup .empty { color: #9eabb0; } /* autocomplete popup */ -.acpopup { - max-height: 150px; + +.acpopup, .acpopup-mce { background-color: #ffffff; - color: #2d2d2d; border: 1px solid #MenuBorder; overflow: auto; z-index: 100000; box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.7); } +acpopup-mce { + color: #2d2d2d; + max-height: 150px; +} .acpopupitem { color: #2d2d2d; padding: 4px; @@ -921,6 +924,16 @@ ul.menu-popup .empty { .acpopupitem.selected { background-color: #bdcdd4; } +.textcomplete-item { + float: none; +} +.textcomplete-item a { + color: #737373; +} +.textcomplete-item a:hover { + padding: 3px 20px; +} + #nav-notifications-menu { width: 400px; max-height: 550px; From 39a849c113de16a4e1f09a9b197cd2491cf4d6fa Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Fri, 22 Jan 2016 17:47:48 +0100 Subject: [PATCH 04/17] rework autocomplete: new ac for poke, contacts, prv_messages --- include/acl_selectors.php | 43 +- library/jquery_ac/README | 4 - library/jquery_ac/friendica.complete.js | 395 ------------------ library/jquery_ac/friendica.complete.min.js | 10 - library/jquery_ac/jquery-1.3.2.min.js | 19 - library/jquery_ac/jquery.autocomplete-min.js | 11 - library/jquery_ac/jquery.autocomplete.js | 390 ----------------- library/jquery_ac/shadow.png | Bin 3403 -> 0 bytes library/jquery_ac/styles.css | 6 - view/templates/contacts-head.tpl | 27 +- view/templates/head.tpl | 1 - view/templates/message-head.tpl | 20 +- view/templates/poke_head.tpl | 20 +- view/theme/frost-mobile/js/theme.js | 26 +- .../frost-mobile/templates/contacts-end.tpl | 3 - .../frost-mobile/templates/message-end.tpl | 3 - view/theme/frost/js/theme.js | 26 +- view/theme/frost/templates/contacts-end.tpl | 3 - view/theme/frost/templates/message-end.tpl | 3 - 19 files changed, 63 insertions(+), 947 deletions(-) delete mode 100644 library/jquery_ac/README delete mode 100644 library/jquery_ac/friendica.complete.js delete mode 100644 library/jquery_ac/friendica.complete.min.js delete mode 100644 library/jquery_ac/jquery-1.3.2.min.js delete mode 100644 library/jquery_ac/jquery.autocomplete-min.js delete mode 100644 library/jquery_ac/jquery.autocomplete.js delete mode 100644 library/jquery_ac/shadow.png delete mode 100644 library/jquery_ac/styles.css diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 19197981e..d78393aee 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -257,7 +257,7 @@ function prune_deadguys($arr) { if(! $arr) return $arr; $str = dbesc(implode(',',$arr)); - $r = q("select id from contact where id in ( " . $str . ") and blocked = 0 and pending = 0 and archive = 0 "); + $r = q("SELECT `id` FROM `contact` WHERE `id` IN ( " . $str . ") AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0 "); if($r) { $ret = array(); foreach($r as $rr) @@ -399,7 +399,7 @@ function acl_lookup(&$a, $out_type = 'json') { $type = (x($_REQUEST,'type')?$_REQUEST['type']:""); $conv_id = (x($_REQUEST,'conversation')?$_REQUEST['conversation']:null); - // For use with jquery.autocomplete for private mail completion + // For use with jquery.textcomplete for private mail completion if(x($_REQUEST,'query') && strlen($_REQUEST['query'])) { if(! $type) @@ -428,6 +428,7 @@ function acl_lookup(&$a, $out_type = 'json') { $sql_extra2 .= " ".unavailable_networks(); + // autocomplete for editor mentions if ($type=='' || $type=='c'){ $r = q("SELECT COUNT(*) AS c FROM `contact` WHERE `uid` = %d AND `self` = 0 @@ -476,7 +477,7 @@ function acl_lookup(&$a, $out_type = 'json') { if ($type=='' || $type=='g'){ - $r = q("SELECT `group`.`id`, `group`.`name`, GROUP_CONCAT(DISTINCT `group_member`.`contact-id` SEPARATOR ',') as uids + $r = q("SELECT `group`.`id`, `group`.`name`, GROUP_CONCAT(DISTINCT `group_member`.`contact-id` SEPARATOR ',') AS uids FROM `group`,`group_member` WHERE `group`.`deleted` = 0 AND `group`.`uid` = %d AND `group_member`.`gid`=`group`.`id` @@ -505,7 +506,7 @@ function acl_lookup(&$a, $out_type = 'json') { if ($type==''){ - $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, forum FROM `contact` + $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0 AND `notify` != '' AND NOT (`network` IN ('%s', '%s')) $sql_extra2 @@ -516,7 +517,7 @@ function acl_lookup(&$a, $out_type = 'json') { } elseif ($type=='c'){ - $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, forum FROM `contact` + $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0 AND `notify` != '' AND NOT (`network` IN ('%s')) $sql_extra2 @@ -546,6 +547,7 @@ function acl_lookup(&$a, $out_type = 'json') { ); } elseif($type == 'x') { + // autocomplete for global contact search (e.g. navbar search) $r = navbar_complete($a); $contacts = array(); if($r) { @@ -569,25 +571,6 @@ function acl_lookup(&$a, $out_type = 'json') { $r = array(); - if($type == 'm' || $type == 'a') { - $x = array(); - $x['query'] = $search; - $x['photos'] = array(); - $x['links'] = array(); - $x['suggestions'] = array(); - $x['data'] = array(); - if(count($r)) { - foreach($r as $g) { - $x['photos'][] = proxy_url($g['micro'], false, PROXY_SIZE_MICRO); - $x['links'][] = $g['url']; - $x['suggestions'][] = htmlentities($g['name']); - $x['data'][] = intval($g['id']); - } - } - echo json_encode($x); - killme(); - } - if(count($r)) { foreach($r as $g){ $contacts[] = array( @@ -611,14 +594,10 @@ function acl_lookup(&$a, $out_type = 'json') { function _contact_link($i){ return dbesc($i['link']); } $known_contacts = array_map(_contact_link, $contacts); $unknow_contacts=array(); - $r = q("select - `author-avatar`,`author-name`,`author-link` - from item where parent=%d - and ( - `author-name` LIKE '%%%s%%' OR - `author-link` LIKE '%%%s%%' - ) and - `author-link` NOT IN ('%s') + $r = q("SELECT `author-avatar`,`author-name`,`author-link` + FROM `item` WHERE `parent` = %d + AND (`author-name` LIKE '%%%s%%' OR `author-link` LIKE '%%%s%%') + AND `author-link` NOT IN ('%s') GROUP BY `author-link` ORDER BY `author-name` ASC ", diff --git a/library/jquery_ac/README b/library/jquery_ac/README deleted file mode 100644 index 422e3d70b..000000000 --- a/library/jquery_ac/README +++ /dev/null @@ -1,4 +0,0 @@ -This is jquery.autocomplete from - -http://www.devbridge.com/projects/autocomplete/jquery/ - diff --git a/library/jquery_ac/friendica.complete.js b/library/jquery_ac/friendica.complete.js deleted file mode 100644 index 81eba564e..000000000 --- a/library/jquery_ac/friendica.complete.js +++ /dev/null @@ -1,395 +0,0 @@ -/** -* Ajax Autocomplete for jQuery, version 1.1.3 -* (c) 2010 Tomas Kirda -* -* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. -* For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/ -* -* Last Review: 04/19/2010 -* Heavily modified for contact completion in Friendica (add photos, hover tips. etc.) 11-May-2012 mike@macgirvin.com -*/ - -/*jslint onevar: true, evil: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */ -/*global window: true, document: true, clearInterval: true, setInterval: true, jQuery: true */ - -(function($) { - - var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g'); - - function fnFormatResult(value, data, currentValue) { - var pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')'; - return value.replace(new RegExp(pattern, 'gi'), '$1<\/strong>'); - } - - function Autocomplete(el, options) { - this.el = $(el); - this.el.attr('autocomplete', 'off'); - this.suggestions = []; - this.data = []; - this.badQueries = []; - this.selectedIndex = -1; - this.currentValue = this.el.val(); - this.intervalId = 0; - this.cachedResponse = []; - this.onChangeInterval = null; - this.ignoreValueChange = false; - this.serviceUrl = options.serviceUrl; - this.isLocal = false; - this.options = { - autoSubmit: false, - minChars: 1, - maxHeight: 300, - deferRequestBy: 0, - width: 0, - highlight: true, - params: {}, - fnFormatResult: fnFormatResult, - delimiter: null, - zIndex: 9999 - }; - this.initialize(); - this.setOptions(options); - } - - $.fn.autocomplete = function(options) { - return new Autocomplete(this.get(0)||$(''), options); - }; - - - Autocomplete.prototype = { - - killerFn: null, - - initialize: function() { - - var me, uid, autocompleteElId; - me = this; - uid = Math.floor(Math.random()*0x100000).toString(16); - autocompleteElId = 'Autocomplete_' + uid; - - this.killerFn = function(e) { - if ($(e.target).parents('.autocomplete').size() === 0) { - me.killSuggestions(); - me.disableKillerFn(); - } - }; - - if (!this.options.width) { this.options.width = this.el.width(); } - this.mainContainerId = 'AutocompleteContainter_' + uid; - - $('
    ').appendTo('body'); - - this.container = $('#' + autocompleteElId); - this.fixPosition(); - if (window.opera) { - this.el.keypress(function(e) { me.onKeyPress(e); }); - } else { - this.el.keydown(function(e) { me.onKeyPress(e); }); - } - this.el.keyup(function(e) { me.onKeyUp(e); }); - this.el.blur(function() { me.enableKillerFn(); }); - this.el.focus(function() { me.fixPosition(); }); - }, - - setOptions: function(options){ - var o = this.options; - $.extend(o, options); - if(o.lookup){ - this.isLocal = true; - if($.isArray(o.lookup)){ o.lookup = { suggestions:o.lookup, data:[] }; } - } - $('#'+this.mainContainerId).css({ zIndex:o.zIndex }); - this.container.css({ maxHeight: o.maxHeight + 'px', width:o.width }); - }, - - clearCache: function(){ - this.cachedResponse = []; - this.badQueries = []; - }, - - disable: function(){ - this.disabled = true; - }, - - enable: function(){ - this.disabled = false; - }, - - fixPosition: function() { - var offset = this.el.offset(); - $('#' + this.mainContainerId).css({ top: (offset.top + this.el.innerHeight()) + 'px', left: offset.left + 'px' }); - }, - - enableKillerFn: function() { - var me = this; - $(document).bind('click', me.killerFn); - }, - - disableKillerFn: function() { - var me = this; - $(document).unbind('click', me.killerFn); - }, - - killSuggestions: function() { - var me = this; - this.stopKillSuggestions(); - this.intervalId = window.setInterval(function() { me.hide(); me.stopKillSuggestions(); }, 300); - }, - - stopKillSuggestions: function() { - window.clearInterval(this.intervalId); - }, - - onKeyPress: function(e) { - if (this.disabled || !this.enabled) { return; } - // return will exit the function - // and event will not be prevented - switch (e.keyCode) { - case 27: //KEY_ESC: - this.el.val(this.currentValue); - this.hide(); - break; - case 9: //KEY_TAB: - case 13: //KEY_RETURN: - if (this.selectedIndex === -1) { - this.hide(); - return; - } - this.select(this.selectedIndex); - if(e.keyCode === 9){ return; } - break; - case 38: //KEY_UP: - this.moveUp(); - break; - case 40: //KEY_DOWN: - this.moveDown(); - break; - default: - return; - } - e.stopImmediatePropagation(); - e.preventDefault(); - }, - - onKeyUp: function(e) { - if(this.disabled){ return; } - switch (e.keyCode) { - case 38: //KEY_UP: - case 40: //KEY_DOWN: - return; - } - clearInterval(this.onChangeInterval); - if (this.currentValue !== this.el.val()) { - if (this.options.deferRequestBy > 0) { - // Defer lookup in case when value changes very quickly: - var me = this; - this.onChangeInterval = setInterval(function() { me.onValueChange(); }, this.options.deferRequestBy); - } else { - this.onValueChange(); - } - } - }, - - onValueChange: function() { - clearInterval(this.onChangeInterval); - this.currentValue = this.el.val(); - var q = this.getQuery(this.currentValue); - this.selectedIndex = -1; - if (this.ignoreValueChange) { - this.ignoreValueChange = false; - return; - } - if (q === '' || q.length < this.options.minChars) { - this.hide(); - } else { - this.getSuggestions(q); - } - }, - - getQuery: function(val) { - var d, arr; - d = this.options.delimiter; - if (!d) { return $.trim(val); } - arr = val.split(d); - return $.trim(arr[arr.length - 1]); - }, - - getSuggestionsLocal: function(q) { - var ret, arr, len, val, i; - arr = this.options.lookup; - len = arr.suggestions.length; - ret = { suggestions:[], data:[] }; - q = q.toLowerCase(); - for(i=0; i< len; i++){ - val = arr.suggestions[i]; - if(val.toLowerCase().indexOf(q) === 0){ - ret.suggestions.push(val); - ret.data.push(arr.data[i]); - } - } - return ret; - }, - - getSuggestions: function(q) { - var cr, me; - cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q]; - if (cr && $.isArray(cr.suggestions)) { - this.suggestions = cr.suggestions; - this.data = cr.data; - this.suggest(); - } else if (!this.isBadQuery(q)) { - me = this; - me.options.params.query = q; - $.get(this.serviceUrl, me.options.params, function(txt) { me.processResponse(txt); }, 'text'); - } - }, - - isBadQuery: function(q) { - var i = this.badQueries.length; - while (i--) { - if (q.indexOf(this.badQueries[i]) === 0) { return true; } - } - return false; - }, - - hide: function() { - this.enabled = false; - this.selectedIndex = -1; - this.container.hide(); - }, - - suggest: function() { - if (this.suggestions.length === 0) { - this.hide(); - return; - } - - var me, len, div, f, v, i, s, mOver, mClick, l, img; - me = this; - len = this.suggestions.length; - f = this.options.fnFormatResult; - v = this.getQuery(this.currentValue); - mOver = function(xi) { return function() { me.activate(xi); }; }; - mClick = function(xi) { return function() { me.select(xi); }; }; - this.container.hide().empty(); - for (i = 0; i < len; i++) { - s = this.suggestions[i]; - l = this.links[i]; - img = '' + s + ' '; - div = $((me.selectedIndex === i ? '
    ' + img + f(s, this.data[i], v) + '
    '); - div.mouseover(mOver(i)); - div.click(mClick(i)); - this.container.append(div); - } - this.enabled = true; - this.container.show(); - }, - - processResponse: function(text) { - var response; - try { - response = eval('(' + text + ')'); - } catch (err) { return; } - if (!$.isArray(response.data)) { response.data = []; } - if(!this.options.noCache){ - this.cachedResponse[response.query] = response; - if (response.suggestions.length === 0) { this.badQueries.push(response.query); } - } - if (response.query === this.getQuery(this.currentValue)) { - this.photos = response.photos; - this.links = response.links; - this.suggestions = response.suggestions; - this.data = response.data; - this.suggest(); - } - }, - - activate: function(index) { - var divs, activeItem; - divs = this.container.children(); - // Clear previous selection: - if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) { - $(divs.get(this.selectedIndex)).removeClass(); - } - this.selectedIndex = index; - if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) { - activeItem = divs.get(this.selectedIndex); - $(activeItem).addClass('selected'); - } - return activeItem; - }, - - deactivate: function(div, index) { - div.className = ''; - if (this.selectedIndex === index) { this.selectedIndex = -1; } - }, - - select: function(i) { - var selectedValue, f; - selectedValue = this.suggestions[i]; - if (selectedValue) { - this.el.val(selectedValue); - if (this.options.autoSubmit) { - f = this.el.parents('form'); - if (f.length > 0) { f.get(0).submit(); } - } - this.ignoreValueChange = true; - this.hide(); - this.onSelect(i); - } - }, - - moveUp: function() { - if (this.selectedIndex === -1) { return; } - if (this.selectedIndex === 0) { - this.container.children().get(0).className = ''; - this.selectedIndex = -1; - this.el.val(this.currentValue); - return; - } - this.adjustScroll(this.selectedIndex - 1); - }, - - moveDown: function() { - if (this.selectedIndex === (this.suggestions.length - 1)) { return; } - this.adjustScroll(this.selectedIndex + 1); - }, - - adjustScroll: function(i) { - var activeItem, offsetTop, upperBound, lowerBound; - activeItem = this.activate(i); - offsetTop = activeItem.offsetTop; - upperBound = this.container.scrollTop(); - lowerBound = upperBound + this.options.maxHeight - 25; - if (offsetTop < upperBound) { - this.container.scrollTop(offsetTop); - } else if (offsetTop > lowerBound) { - this.container.scrollTop(offsetTop - this.options.maxHeight + 25); - } - this.el.val(this.getValue(this.suggestions[i])); - }, - - onSelect: function(i) { - var me, fn, s, d; - me = this; - fn = me.options.onSelect; - s = me.suggestions[i]; - d = me.data[i]; - me.el.val(me.getValue(s)); - if ($.isFunction(fn)) { fn(s, d, me.el); } - }, - - getValue: function(value){ - var del, currVal, arr, me; - me = this; - del = me.options.delimiter; - if (!del) { return value; } - currVal = me.currentValue; - arr = currVal.split(del); - if (arr.length === 1) { return value; } - return currVal.substr(0, currVal.length - arr[arr.length - 1].length) + value; - } - - }; - -}(jQuery)); diff --git a/library/jquery_ac/friendica.complete.min.js b/library/jquery_ac/friendica.complete.min.js deleted file mode 100644 index c879832eb..000000000 --- a/library/jquery_ac/friendica.complete.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/** -* Ajax Autocomplete for jQuery, version 1.1.3 -* (c) 2010 Tomas Kirda -* -* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. -* For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/ -* -* Last Review: 04/19/2010 -* Heavily modified for contact completion in Friendica (add photos, hover tips. etc.) 11-May-2012 mike@macgirvin.com -*//*jslint onevar: true, evil: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true *//*global window: true, document: true, clearInterval: true, setInterval: true, jQuery: true */(function($){function fnFormatResult(value,data,currentValue){var pattern="("+currentValue.replace(reEscape,"\\$1")+")";return value.replace(new RegExp(pattern,"gi"),"$1")}function Autocomplete(el,options){this.el=$(el),this.el.attr("autocomplete","off"),this.suggestions=[],this.data=[],this.badQueries=[],this.selectedIndex=-1,this.currentValue=this.el.val(),this.intervalId=0,this.cachedResponse=[],this.onChangeInterval=null,this.ignoreValueChange=!1,this.serviceUrl=options.serviceUrl,this.isLocal=!1,this.options={autoSubmit:!1,minChars:1,maxHeight:300,deferRequestBy:0,width:0,highlight:!0,params:{},fnFormatResult:fnFormatResult,delimiter:null,zIndex:9999},this.initialize(),this.setOptions(options)}var reEscape=new RegExp("(\\"+["/",".","*","+","?","|","(",")","[","]","{","}","\\"].join("|\\")+")","g");$.fn.autocomplete=function(options){return new Autocomplete(this.get(0)||$(""),options)},Autocomplete.prototype={killerFn:null,initialize:function(){var me,uid,autocompleteElId;me=this,uid=Math.floor(Math.random()*1048576).toString(16),autocompleteElId="Autocomplete_"+uid,this.killerFn=function(e){$(e.target).parents(".autocomplete").size()===0&&(me.killSuggestions(),me.disableKillerFn())},this.options.width||(this.options.width=this.el.width()),this.mainContainerId="AutocompleteContainter_"+uid,$('
    ').appendTo("body"),this.container=$("#"+autocompleteElId),this.fixPosition(),window.opera?this.el.keypress(function(e){me.onKeyPress(e)}):this.el.keydown(function(e){me.onKeyPress(e)}),this.el.keyup(function(e){me.onKeyUp(e)}),this.el.blur(function(){me.enableKillerFn()}),this.el.focus(function(){me.fixPosition()})},setOptions:function(options){var o=this.options;$.extend(o,options),o.lookup&&(this.isLocal=!0,$.isArray(o.lookup)&&(o.lookup={suggestions:o.lookup,data:[]})),$("#"+this.mainContainerId).css({zIndex:o.zIndex}),this.container.css({maxHeight:o.maxHeight+"px",width:o.width})},clearCache:function(){this.cachedResponse=[],this.badQueries=[]},disable:function(){this.disabled=!0},enable:function(){this.disabled=!1},fixPosition:function(){var offset=this.el.offset();$("#"+this.mainContainerId).css({top:offset.top+this.el.innerHeight()+"px",left:offset.left+"px"})},enableKillerFn:function(){var me=this;$(document).bind("click",me.killerFn)},disableKillerFn:function(){var me=this;$(document).unbind("click",me.killerFn)},killSuggestions:function(){var me=this;this.stopKillSuggestions(),this.intervalId=window.setInterval(function(){me.hide(),me.stopKillSuggestions()},300)},stopKillSuggestions:function(){window.clearInterval(this.intervalId)},onKeyPress:function(e){if(this.disabled||!this.enabled)return;switch(e.keyCode){case 27:this.el.val(this.currentValue),this.hide();break;case 9:case 13:if(this.selectedIndex===-1){this.hide();return}this.select(this.selectedIndex);if(e.keyCode===9)return;break;case 38:this.moveUp();break;case 40:this.moveDown();break;default:return}e.stopImmediatePropagation(),e.preventDefault()},onKeyUp:function(e){if(this.disabled)return;switch(e.keyCode){case 38:case 40:return}clearInterval(this.onChangeInterval);if(this.currentValue!==this.el.val())if(this.options.deferRequestBy>0){var me=this;this.onChangeInterval=setInterval(function(){me.onValueChange()},this.options.deferRequestBy)}else this.onValueChange()},onValueChange:function(){clearInterval(this.onChangeInterval),this.currentValue=this.el.val();var q=this.getQuery(this.currentValue);this.selectedIndex=-1;if(this.ignoreValueChange){this.ignoreValueChange=!1;return}q===""||q.length ',div=$((me.selectedIndex===i?'
    '+img+f(s,this.data[i],v)+"
    "),div.mouseover(mOver(i)),div.click(mClick(i)),this.container.append(div);this.enabled=!0,this.container.show()},processResponse:function(text){var response;try{response=eval("("+text+")")}catch(err){return}$.isArray(response.data)||(response.data=[]),this.options.noCache||(this.cachedResponse[response.query]=response,response.suggestions.length===0&&this.badQueries.push(response.query)),response.query===this.getQuery(this.currentValue)&&(this.photos=response.photos,this.links=response.links,this.suggestions=response.suggestions,this.data=response.data,this.suggest())},activate:function(index){var divs,activeItem;return divs=this.container.children(),this.selectedIndex!==-1&&divs.length>this.selectedIndex&&$(divs.get(this.selectedIndex)).removeClass(),this.selectedIndex=index,this.selectedIndex!==-1&&divs.length>this.selectedIndex&&(activeItem=divs.get(this.selectedIndex),$(activeItem).addClass("selected")),activeItem},deactivate:function(div,index){div.className="",this.selectedIndex===index&&(this.selectedIndex=-1)},select:function(i){var selectedValue,f;selectedValue=this.suggestions[i],selectedValue&&(this.el.val(selectedValue),this.options.autoSubmit&&(f=this.el.parents("form"),f.length>0&&f.get(0).submit()),this.ignoreValueChange=!0,this.hide(),this.onSelect(i))},moveUp:function(){if(this.selectedIndex===-1)return;if(this.selectedIndex===0){this.container.children().get(0).className="",this.selectedIndex=-1,this.el.val(this.currentValue);return}this.adjustScroll(this.selectedIndex-1)},moveDown:function(){if(this.selectedIndex===this.suggestions.length-1)return;this.adjustScroll(this.selectedIndex+1)},adjustScroll:function(i){var activeItem,offsetTop,upperBound,lowerBound;activeItem=this.activate(i),offsetTop=activeItem.offsetTop,upperBound=this.container.scrollTop(),lowerBound=upperBound+this.options.maxHeight-25,offsetToplowerBound&&this.container.scrollTop(offsetTop-this.options.maxHeight+25),this.el.val(this.getValue(this.suggestions[i]))},onSelect:function(i){var me,fn,s,d;me=this,fn=me.options.onSelect,s=me.suggestions[i],d=me.data[i],me.el.val(me.getValue(s)),$.isFunction(fn)&&fn(s,d,me.el)},getValue:function(value){var del,currVal,arr,me;return me=this,del=me.options.delimiter,del?(currVal=me.currentValue,arr=currVal.split(del),arr.length===1?value:currVal.substr(0,currVal.length-arr[arr.length-1].length)+value):value}}})(jQuery); \ No newline at end of file diff --git a/library/jquery_ac/jquery-1.3.2.min.js b/library/jquery_ac/jquery-1.3.2.min.js deleted file mode 100644 index b1ae21d8b..000000000 --- a/library/jquery_ac/jquery-1.3.2.min.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
    "]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
    ","
    "]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); -/* - * Sizzle CSS Selector Engine - v0.9.3 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

    ";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
    ";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
    ").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
    ';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/library/jquery_ac/jquery.autocomplete-min.js b/library/jquery_ac/jquery.autocomplete-min.js deleted file mode 100644 index 7018fd00a..000000000 --- a/library/jquery_ac/jquery.autocomplete-min.js +++ /dev/null @@ -1,11 +0,0 @@ -/** -* Ajax Autocomplete for jQuery, version 1.1.3 -* (c) 2010 Tomas Kirda -* -* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. -* For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/ -* -* Last Review: 04/19/2010 -*/ - -(function(d){function l(b,a,c){a="("+c.replace(m,"\\$1")+")";return b.replace(new RegExp(a,"gi"),"$1")}function i(b,a){this.el=d(b);this.el.attr("autocomplete","off");this.suggestions=[];this.data=[];this.badQueries=[];this.selectedIndex=-1;this.currentValue=this.el.val();this.intervalId=0;this.cachedResponse=[];this.onChangeInterval=null;this.ignoreValueChange=false;this.serviceUrl=a.serviceUrl;this.isLocal=false;this.options={autoSubmit:false,minChars:1,maxHeight:300,deferRequestBy:0, width:0,highlight:true,params:{},fnFormatResult:l,delimiter:null,zIndex:9999};this.initialize();this.setOptions(a)}var m=new RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\)","g");d.fn.autocomplete=function(b){return new i(this.get(0)||d(""),b)};i.prototype={killerFn:null,initialize:function(){var b,a,c;b=this;a=Math.floor(Math.random()*1048576).toString(16);c="Autocomplete_"+a;this.killerFn=function(e){if(d(e.target).parents(".autocomplete").size()===0){b.killSuggestions(); b.disableKillerFn()}};if(!this.options.width)this.options.width=this.el.width();this.mainContainerId="AutocompleteContainter_"+a;d('
    ').appendTo("body");this.container=d("#"+c);this.fixPosition();window.opera?this.el.keypress(function(e){b.onKeyPress(e)}):this.el.keydown(function(e){b.onKeyPress(e)});this.el.keyup(function(e){b.onKeyUp(e)}); this.el.blur(function(){b.enableKillerFn()});this.el.focus(function(){b.fixPosition()})},setOptions:function(b){var a=this.options;d.extend(a,b);if(a.lookup){this.isLocal=true;if(d.isArray(a.lookup))a.lookup={suggestions:a.lookup,data:[]}}d("#"+this.mainContainerId).css({zIndex:a.zIndex});this.container.css({maxHeight:a.maxHeight+"px",width:a.width})},clearCache:function(){this.cachedResponse=[];this.badQueries=[]},disable:function(){this.disabled=true},enable:function(){this.disabled=false},fixPosition:function(){var b= this.el.offset();d("#"+this.mainContainerId).css({top:b.top+this.el.innerHeight()+"px",left:b.left+"px"})},enableKillerFn:function(){d(document).bind("click",this.killerFn)},disableKillerFn:function(){d(document).unbind("click",this.killerFn)},killSuggestions:function(){var b=this;this.stopKillSuggestions();this.intervalId=window.setInterval(function(){b.hide();b.stopKillSuggestions()},300)},stopKillSuggestions:function(){window.clearInterval(this.intervalId)},onKeyPress:function(b){if(!(this.disabled|| !this.enabled)){switch(b.keyCode){case 27:this.el.val(this.currentValue);this.hide();break;case 9:case 13:if(this.selectedIndex===-1){this.hide();return}this.select(this.selectedIndex);if(b.keyCode===9)return;break;case 38:this.moveUp();break;case 40:this.moveDown();break;default:return}b.stopImmediatePropagation();b.preventDefault()}},onKeyUp:function(b){if(!this.disabled){switch(b.keyCode){case 38:case 40:return}clearInterval(this.onChangeInterval);if(this.currentValue!==this.el.val())if(this.options.deferRequestBy> 0){var a=this;this.onChangeInterval=setInterval(function(){a.onValueChange()},this.options.deferRequestBy)}else this.onValueChange()}},onValueChange:function(){clearInterval(this.onChangeInterval);this.currentValue=this.el.val();var b=this.getQuery(this.currentValue);this.selectedIndex=-1;if(this.ignoreValueChange)this.ignoreValueChange=false;else b===""||b.length'+e(c,this.data[f],g)+"
    ");c.mouseover(j(f));c.click(k(f));this.container.append(c)}this.enabled=true;this.container.show()}},processResponse:function(b){var a;try{a=eval("("+b+")")}catch(c){return}if(!d.isArray(a.data))a.data=[];if(!this.options.noCache){this.cachedResponse[a.query]= a;a.suggestions.length===0&&this.badQueries.push(a.query)}if(a.query===this.getQuery(this.currentValue)){this.suggestions=a.suggestions;this.data=a.data;this.suggest()}},activate:function(b){var a,c;a=this.container.children();this.selectedIndex!==-1&&a.length>this.selectedIndex&&d(a.get(this.selectedIndex)).removeClass();this.selectedIndex=b;if(this.selectedIndex!==-1&&a.length>this.selectedIndex){c=a.get(this.selectedIndex);d(c).addClass("selected")}return c},deactivate:function(b,a){b.className= "";if(this.selectedIndex===a)this.selectedIndex=-1},select:function(b){var a;if(a=this.suggestions[b]){this.el.val(a);if(this.options.autoSubmit){a=this.el.parents("form");a.length>0&&a.get(0).submit()}this.ignoreValueChange=true;this.hide();this.onSelect(b)}},moveUp:function(){if(this.selectedIndex!==-1)if(this.selectedIndex===0){this.container.children().get(0).className="";this.selectedIndex=-1;this.el.val(this.currentValue)}else this.adjustScroll(this.selectedIndex-1)},moveDown:function(){this.selectedIndex!== this.suggestions.length-1&&this.adjustScroll(this.selectedIndex+1)},adjustScroll:function(b){var a,c,e;a=this.activate(b).offsetTop;c=this.container.scrollTop();e=c+this.options.maxHeight-25;if(ae&&this.container.scrollTop(a-this.options.maxHeight+25);this.el.val(this.getValue(this.suggestions[b]))},onSelect:function(b){var a,c;a=this.options.onSelect;c=this.suggestions[b];b=this.data[b];this.el.val(this.getValue(c));d.isFunction(a)&&a(c,b,this.el)},getValue:function(b){var a, c;a=this.options.delimiter;if(!a)return b;c=this.currentValue;a=c.split(a);if(a.length===1)return b;return c.substr(0,c.length-a[a.length-1].length)+b}}})(jQuery); \ No newline at end of file diff --git a/library/jquery_ac/jquery.autocomplete.js b/library/jquery_ac/jquery.autocomplete.js deleted file mode 100644 index 6a7ce8787..000000000 --- a/library/jquery_ac/jquery.autocomplete.js +++ /dev/null @@ -1,390 +0,0 @@ -/** -* Ajax Autocomplete for jQuery, version 1.1.3 -* (c) 2010 Tomas Kirda -* -* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. -* For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/ -* -* Last Review: 04/19/2010 -*/ - -/*jslint onevar: true, evil: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */ -/*global window: true, document: true, clearInterval: true, setInterval: true, jQuery: true */ - -(function($) { - - var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g'); - - function fnFormatResult(value, data, currentValue) { - var pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')'; - return value.replace(new RegExp(pattern, 'gi'), '$1<\/strong>'); - } - - function Autocomplete(el, options) { - this.el = $(el); - this.el.attr('autocomplete', 'off'); - this.suggestions = []; - this.data = []; - this.badQueries = []; - this.selectedIndex = -1; - this.currentValue = this.el.val(); - this.intervalId = 0; - this.cachedResponse = []; - this.onChangeInterval = null; - this.ignoreValueChange = false; - this.serviceUrl = options.serviceUrl; - this.isLocal = false; - this.options = { - autoSubmit: false, - minChars: 1, - maxHeight: 300, - deferRequestBy: 0, - width: 0, - highlight: true, - params: {}, - fnFormatResult: fnFormatResult, - delimiter: null, - zIndex: 9999 - }; - this.initialize(); - this.setOptions(options); - } - - $.fn.autocomplete = function(options) { - return new Autocomplete(this.get(0)||$(''), options); - }; - - - Autocomplete.prototype = { - - killerFn: null, - - initialize: function() { - - var me, uid, autocompleteElId; - me = this; - uid = Math.floor(Math.random()*0x100000).toString(16); - autocompleteElId = 'Autocomplete_' + uid; - - this.killerFn = function(e) { - if ($(e.target).parents('.autocomplete').size() === 0) { - me.killSuggestions(); - me.disableKillerFn(); - } - }; - - if (!this.options.width) { this.options.width = this.el.width(); } - this.mainContainerId = 'AutocompleteContainter_' + uid; - - $('
    ').appendTo('body'); - - this.container = $('#' + autocompleteElId); - this.fixPosition(); - if (window.opera) { - this.el.keypress(function(e) { me.onKeyPress(e); }); - } else { - this.el.keydown(function(e) { me.onKeyPress(e); }); - } - this.el.keyup(function(e) { me.onKeyUp(e); }); - this.el.blur(function() { me.enableKillerFn(); }); - this.el.focus(function() { me.fixPosition(); }); - }, - - setOptions: function(options){ - var o = this.options; - $.extend(o, options); - if(o.lookup){ - this.isLocal = true; - if($.isArray(o.lookup)){ o.lookup = { suggestions:o.lookup, data:[] }; } - } - $('#'+this.mainContainerId).css({ zIndex:o.zIndex }); - this.container.css({ maxHeight: o.maxHeight + 'px', width:o.width }); - }, - - clearCache: function(){ - this.cachedResponse = []; - this.badQueries = []; - }, - - disable: function(){ - this.disabled = true; - }, - - enable: function(){ - this.disabled = false; - }, - - fixPosition: function() { - var offset = this.el.offset(); - $('#' + this.mainContainerId).css({ top: (offset.top + this.el.innerHeight()) + 'px', left: offset.left + 'px' }); - }, - - enableKillerFn: function() { - var me = this; - $(document).bind('click', me.killerFn); - }, - - disableKillerFn: function() { - var me = this; - $(document).unbind('click', me.killerFn); - }, - - killSuggestions: function() { - var me = this; - this.stopKillSuggestions(); - this.intervalId = window.setInterval(function() { me.hide(); me.stopKillSuggestions(); }, 300); - }, - - stopKillSuggestions: function() { - window.clearInterval(this.intervalId); - }, - - onKeyPress: function(e) { - if (this.disabled || !this.enabled) { return; } - // return will exit the function - // and event will not be prevented - switch (e.keyCode) { - case 27: //KEY_ESC: - this.el.val(this.currentValue); - this.hide(); - break; - case 9: //KEY_TAB: - case 13: //KEY_RETURN: - if (this.selectedIndex === -1) { - this.hide(); - return; - } - this.select(this.selectedIndex); - if(e.keyCode === 9){ return; } - break; - case 38: //KEY_UP: - this.moveUp(); - break; - case 40: //KEY_DOWN: - this.moveDown(); - break; - default: - return; - } - e.stopImmediatePropagation(); - e.preventDefault(); - }, - - onKeyUp: function(e) { - if(this.disabled){ return; } - switch (e.keyCode) { - case 38: //KEY_UP: - case 40: //KEY_DOWN: - return; - } - clearInterval(this.onChangeInterval); - if (this.currentValue !== this.el.val()) { - if (this.options.deferRequestBy > 0) { - // Defer lookup in case when value changes very quickly: - var me = this; - this.onChangeInterval = setInterval(function() { me.onValueChange(); }, this.options.deferRequestBy); - } else { - this.onValueChange(); - } - } - }, - - onValueChange: function() { - clearInterval(this.onChangeInterval); - this.currentValue = this.el.val(); - var q = this.getQuery(this.currentValue); - this.selectedIndex = -1; - if (this.ignoreValueChange) { - this.ignoreValueChange = false; - return; - } - if (q === '' || q.length < this.options.minChars) { - this.hide(); - } else { - this.getSuggestions(q); - } - }, - - getQuery: function(val) { - var d, arr; - d = this.options.delimiter; - if (!d) { return $.trim(val); } - arr = val.split(d); - return $.trim(arr[arr.length - 1]); - }, - - getSuggestionsLocal: function(q) { - var ret, arr, len, val, i; - arr = this.options.lookup; - len = arr.suggestions.length; - ret = { suggestions:[], data:[] }; - q = q.toLowerCase(); - for(i=0; i< len; i++){ - val = arr.suggestions[i]; - if(val.toLowerCase().indexOf(q) === 0){ - ret.suggestions.push(val); - ret.data.push(arr.data[i]); - } - } - return ret; - }, - - getSuggestions: function(q) { - var cr, me; - cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q]; - if (cr && $.isArray(cr.suggestions)) { - this.suggestions = cr.suggestions; - this.data = cr.data; - this.suggest(); - } else if (!this.isBadQuery(q)) { - me = this; - me.options.params.query = q; - $.get(this.serviceUrl, me.options.params, function(txt) { me.processResponse(txt); }, 'text'); - } - }, - - isBadQuery: function(q) { - var i = this.badQueries.length; - while (i--) { - if (q.indexOf(this.badQueries[i]) === 0) { return true; } - } - return false; - }, - - hide: function() { - this.enabled = false; - this.selectedIndex = -1; - this.container.hide(); - }, - - suggest: function() { - if (this.suggestions.length === 0) { - this.hide(); - return; - } - - var me, len, div, f, v, i, s, mOver, mClick; - me = this; - len = this.suggestions.length; - f = this.options.fnFormatResult; - v = this.getQuery(this.currentValue); - mOver = function(xi) { return function() { me.activate(xi); }; }; - mClick = function(xi) { return function() { me.select(xi); }; }; - this.container.hide().empty(); - for (i = 0; i < len; i++) { - s = this.suggestions[i]; - div = $((me.selectedIndex === i ? '
    ' + f(s, this.data[i], v) + '
    '); - div.mouseover(mOver(i)); - div.click(mClick(i)); - this.container.append(div); - } - this.enabled = true; - this.container.show(); - }, - - processResponse: function(text) { - var response; - try { - response = eval('(' + text + ')'); - } catch (err) { return; } - if (!$.isArray(response.data)) { response.data = []; } - if(!this.options.noCache){ - this.cachedResponse[response.query] = response; - if (response.suggestions.length === 0) { this.badQueries.push(response.query); } - } - if (response.query === this.getQuery(this.currentValue)) { - this.suggestions = response.suggestions; - this.data = response.data; - this.suggest(); - } - }, - - activate: function(index) { - var divs, activeItem; - divs = this.container.children(); - // Clear previous selection: - if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) { - $(divs.get(this.selectedIndex)).removeClass(); - } - this.selectedIndex = index; - if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) { - activeItem = divs.get(this.selectedIndex); - $(activeItem).addClass('selected'); - } - return activeItem; - }, - - deactivate: function(div, index) { - div.className = ''; - if (this.selectedIndex === index) { this.selectedIndex = -1; } - }, - - select: function(i) { - var selectedValue, f; - selectedValue = this.suggestions[i]; - if (selectedValue) { - this.el.val(selectedValue); - if (this.options.autoSubmit) { - f = this.el.parents('form'); - if (f.length > 0) { f.get(0).submit(); } - } - this.ignoreValueChange = true; - this.hide(); - this.onSelect(i); - } - }, - - moveUp: function() { - if (this.selectedIndex === -1) { return; } - if (this.selectedIndex === 0) { - this.container.children().get(0).className = ''; - this.selectedIndex = -1; - this.el.val(this.currentValue); - return; - } - this.adjustScroll(this.selectedIndex - 1); - }, - - moveDown: function() { - if (this.selectedIndex === (this.suggestions.length - 1)) { return; } - this.adjustScroll(this.selectedIndex + 1); - }, - - adjustScroll: function(i) { - var activeItem, offsetTop, upperBound, lowerBound; - activeItem = this.activate(i); - offsetTop = activeItem.offsetTop; - upperBound = this.container.scrollTop(); - lowerBound = upperBound + this.options.maxHeight - 25; - if (offsetTop < upperBound) { - this.container.scrollTop(offsetTop); - } else if (offsetTop > lowerBound) { - this.container.scrollTop(offsetTop - this.options.maxHeight + 25); - } - this.el.val(this.getValue(this.suggestions[i])); - }, - - onSelect: function(i) { - var me, fn, s, d; - me = this; - fn = me.options.onSelect; - s = me.suggestions[i]; - d = me.data[i]; - me.el.val(me.getValue(s)); - if ($.isFunction(fn)) { fn(s, d, me.el); } - }, - - getValue: function(value){ - var del, currVal, arr, me; - me = this; - del = me.options.delimiter; - if (!del) { return value; } - currVal = me.currentValue; - arr = currVal.split(del); - if (arr.length === 1) { return value; } - return currVal.substr(0, currVal.length - arr[arr.length - 1].length) + value; - } - - }; - -}(jQuery)); diff --git a/library/jquery_ac/shadow.png b/library/jquery_ac/shadow.png deleted file mode 100644 index a2561df971728d988424100c74c817916eca1979..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3403 zcmeAS@N?(olHy`uVBq!ia0y~yV738bR}L1Sh{z?w93Z7#;u=xnT$Gwvl9`{U5R#dj z$`F!Ks$i<%mYSqsWME*TU}$J%1Vly(x&~$j21QvmX+Ul4C7!;n>{pmr1x4l8+mz-4 zg*Xd5B8wRqxP?HN@zUM8KR`j2bVpxD28NCO+HqZdXMAQjaPeGm)a##I4DP>8^|Q}*osX?x zu(w4E^8SQ>2<4nWJ;;$Ch1?$dLgm)?6;Yr9_CdHMW*W(@x+ip*R06EH_T4pml(Y0_%+Z_b^VZki z+Ig-L)GH{z-FHJSJv;l^O{dMW8|Geryoiy(edpWebG3Q>oX-o7q}^hCpz*y@X6IS? ZpGWQ1{0Pup3+%oyc)I$ztaD0e0swh%>OlYi diff --git a/library/jquery_ac/styles.css b/library/jquery_ac/styles.css deleted file mode 100644 index e056d5cf9..000000000 --- a/library/jquery_ac/styles.css +++ /dev/null @@ -1,6 +0,0 @@ - -.autocomplete-w1 { background:url(img/shadow.png) no-repeat bottom right; position:absolute; top:0px; left:0px; margin:8px 0 0 6px; /* IE6 fix: */ _background:none; _margin:0; } -.autocomplete { border:1px solid #999; background:#FFF; cursor:default; text-align:left; max-height:350px; overflow:auto; margin:-6px 6px 6px -6px; /* IE6 specific: */ _height:350px; _margin:0; _overflow-x:hidden; } -.autocomplete .selected { background:#F0F0F0; } -.autocomplete div { padding:2px 5px; white-space:nowrap; } -.autocomplete strong { font-weight:normal; color:#3399FF; } diff --git a/view/templates/contacts-head.tpl b/view/templates/contacts-head.tpl index f054fdce2..25055d405 100644 --- a/view/templates/contacts-head.tpl +++ b/view/templates/contacts-head.tpl @@ -1,18 +1,19 @@ - - - diff --git a/view/templates/head.tpl b/view/templates/head.tpl index 129e5cfd2..afb7c1df2 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -35,7 +35,6 @@ - diff --git a/view/templates/message-head.tpl b/view/templates/message-head.tpl index 875664628..49f268c42 100644 --- a/view/templates/message-head.tpl +++ b/view/templates/message-head.tpl @@ -1,18 +1,8 @@ - - - - diff --git a/view/templates/poke_head.tpl b/view/templates/poke_head.tpl index 6e30eb7fe..7de98d221 100644 --- a/view/templates/poke_head.tpl +++ b/view/templates/poke_head.tpl @@ -1,18 +1,8 @@ - - \ No newline at end of file + diff --git a/view/theme/frost-mobile/js/theme.js b/view/theme/frost-mobile/js/theme.js index c02137087..7d7e8259c 100644 --- a/view/theme/frost-mobile/js/theme.js +++ b/view/theme/frost-mobile/js/theme.js @@ -103,22 +103,24 @@ $(document).ready(function() { switch(window.autocompleteType) { case 'msg-header': - var a = $("#recip").autocomplete({ - serviceUrl: baseurl + '/acl', - minChars: 2, - width: 350, - onSelect: function(value,data) { - $("#recip-complete").val(data); - } + $("#recip").name_autocomplete(baseurl + '/acl', '', false, function(data) { + $("#recip-complete").val(data.id); }); break; case 'contacts-head': - var a = $("#contacts-search").autocomplete({ - serviceUrl: baseurl + '/acl', - minChars: 2, - width: 350, + $("#contacts-search").contact_autocomplete(baseurl + '/acl', 'a', true); + + + $("#contacts-search").keyup(function(event){ + if(event.keyCode == 13){ + $("#contacts-search").click(); + } + }); + $(".autocomplete-w1 .selected").keyup(function(event){ + if(event.keyCode == 13){ + $("#contacts-search").click(); + } }); - a.setOptions({ params: { type: 'a' }}); break; case 'display-head': $(".comment-wwedit-wrapper textarea").editor_autocomplete(baseurl+"/acl"); diff --git a/view/theme/frost-mobile/templates/contacts-end.tpl b/view/theme/frost-mobile/templates/contacts-end.tpl index 5416f872c..139597f9c 100644 --- a/view/theme/frost-mobile/templates/contacts-end.tpl +++ b/view/theme/frost-mobile/templates/contacts-end.tpl @@ -1,5 +1,2 @@ - - - diff --git a/view/theme/frost-mobile/templates/message-end.tpl b/view/theme/frost-mobile/templates/message-end.tpl index 5416f872c..139597f9c 100644 --- a/view/theme/frost-mobile/templates/message-end.tpl +++ b/view/theme/frost-mobile/templates/message-end.tpl @@ -1,5 +1,2 @@ - - - diff --git a/view/theme/frost/js/theme.js b/view/theme/frost/js/theme.js index 4418c1f3c..fc1bb643c 100644 --- a/view/theme/frost/js/theme.js +++ b/view/theme/frost/js/theme.js @@ -214,22 +214,24 @@ $(document).ready(function() { switch(window.autocompleteType) { case 'msg-header': - var a = $("#recip").autocomplete({ - serviceUrl: baseurl + '/acl', - minChars: 2, - width: 350, - onSelect: function(value,data) { - $("#recip-complete").val(data); - } + $("#recip").name_autocomplete(baseurl + '/acl', '', false, function(data) { + $("#recip-complete").val(data.id); }); break; case 'contacts-head': - var a = $("#contacts-search").autocomplete({ - serviceUrl: baseurl + '/acl', - minChars: 2, - width: 350, + $("#contacts-search").contact_autocomplete(baseurl + '/acl', 'a', true); + + + $("#contacts-search").keyup(function(event){ + if(event.keyCode == 13){ + $("#contacts-search").click(); + } + }); + $(".autocomplete-w1 .selected").keyup(function(event){ + if(event.keyCode == 13){ + $("#contacts-search").click(); + } }); - a.setOptions({ params: { type: 'a' }}); break; case 'display-head': $(".comment-wwedit-wrapper textarea").editor_autocomplete(baseurl+"/acl"); diff --git a/view/theme/frost/templates/contacts-end.tpl b/view/theme/frost/templates/contacts-end.tpl index 5416f872c..139597f9c 100644 --- a/view/theme/frost/templates/contacts-end.tpl +++ b/view/theme/frost/templates/contacts-end.tpl @@ -1,5 +1,2 @@ - - - diff --git a/view/theme/frost/templates/message-end.tpl b/view/theme/frost/templates/message-end.tpl index 5416f872c..139597f9c 100644 --- a/view/theme/frost/templates/message-end.tpl +++ b/view/theme/frost/templates/message-end.tpl @@ -1,5 +1,2 @@ - - - From d0ade828c05545e45ce129c05090fa795d0376c1 Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Sun, 24 Jan 2016 19:56:23 +0100 Subject: [PATCH 05/17] rework autocomplete: some cleanup and docu --- include/acl_selectors.php | 45 ++++++++---------- include/dir_fns.php | 18 ++++++-- include/nav.php | 59 +++++++++++------------- view/global.css | 2 - view/templates/message-head.tpl | 2 +- view/templates/nav_head.tpl | 0 view/theme/vier/breathe.css | 2 +- view/theme/vier/narrow.css | 2 +- view/theme/vier/plus.css | 2 +- view/theme/vier/shadow.css | 4 +- view/theme/vier/style.css | 64 ++++++++++++++------------ view/theme/vier/templates/nav.tpl | 4 +- view/theme/vier/templates/nav_head.tpl | 6 +++ 13 files changed, 107 insertions(+), 103 deletions(-) create mode 100644 view/templates/nav_head.tpl create mode 100644 view/theme/vier/templates/nav_head.tpl diff --git a/include/acl_selectors.php b/include/acl_selectors.php index d78393aee..46f737e4c 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -1,13 +1,15 @@ pager['page'] != 1) ? '&p=' . $a->pager['page'] : ''); - $x = z_fetch_url(get_server().'/lsearch?f=' . $p . '&search=' . urlencode($search)); if($x['success']) { diff --git a/include/dir_fns.php b/include/dir_fns.php index a362732b7..2e39e7ddb 100644 --- a/include/dir_fns.php +++ b/include/dir_fns.php @@ -1,11 +1,20 @@ page,'nav'))) $a->page['nav'] = ''; - $base = z_root(); - /** + $a->page['htmlhead'] .= replace_macros(get_markup_template('nav_head.tpl'), array()); + + /* * Placeholder div for popup panel */ - $a->page['htmlhead'] .= <<< EOT - - -EOT; $a->page['nav'] .= '' ; $nav_info = nav_info($a); - /** + /* * Build the page */ @@ -37,12 +31,13 @@ EOT; '$baseurl' => $a->get_baseurl(), '$sitelocation' => $nav_info['sitelocation'], '$nav' => $nav_info['nav'], - '$banner' => $nav_info['banner'], + '$banner' => $nav_info['banner'], '$emptynotifications' => t('Nothing new here'), '$userinfo' => $nav_info['userinfo'], - '$sel' => $a->nav_sel, + '$sel' => $a->nav_sel, '$apps' => $a->apps, - '$clear_notifs' => t('Clear notifications') + '$clear_notifs' => t('Clear notifications'), + '$search_hint' => t('@name, !forum, #tags, content') )); call_hooks('page_header', $a->page['nav']); @@ -53,7 +48,7 @@ function nav_info(&$a) { $ssl_state = ((local_user()) ? true : false); - /** + /* * * Our network is distributed, and as you visit friends some of the * sites look exactly the same - it isn't always easy to know where you are. @@ -69,7 +64,7 @@ function nav_info(&$a) { // nav links: array of array('href', 'text', 'extra css classes', 'title') $nav = Array(); - /** + /* * Display login or logout */ @@ -100,7 +95,7 @@ function nav_info(&$a) { } - /** + /* * "Home" should also take you home from an authenticated remote profile connection */ @@ -151,7 +146,7 @@ function nav_info(&$a) { $nav['about'] = Array('friendica', t('Information'), "", t('Information about this friendica instance')); - /** + /* * * The following nav links are only show to logged in users * @@ -195,7 +190,7 @@ function nav_info(&$a) { $nav['contacts'] = array('contacts', t('Contacts'),"", t('Manage/edit friends and contacts')); } - /** + /* * Admin page */ if (is_site_admin()){ @@ -206,7 +201,7 @@ function nav_info(&$a) { $nav['navigation'] = array('navigation/', t('Navigation'), "", t('Site map')); - /** + /* * * Provide a banner/logo/whatever * @@ -229,26 +224,26 @@ function nav_info(&$a) { } -/* +/** * Set a menu item in navbar as selected * */ function nav_set_selected($item){ $a = get_app(); - $a->nav_sel = array( + $a->nav_sel = array( 'community' => null, - 'network' => null, - 'home' => null, - 'profiles' => null, + 'network' => null, + 'home' => null, + 'profiles' => null, 'introductions' => null, 'notifications' => null, - 'messages' => null, - 'directory' => null, - 'settings' => null, - 'contacts' => null, - 'manage' => null, - 'events' => null, - 'register' => null, + 'messages' => null, + 'directory' => null, + 'settings' => null, + 'contacts' => null, + 'manage' => null, + 'events' => null, + 'register' => null, ); $a->nav_sel[$item] = 'selected'; } diff --git a/view/global.css b/view/global.css index ff2810292..7fa9947d3 100644 --- a/view/global.css +++ b/view/global.css @@ -184,7 +184,6 @@ key { display: inline; background-color: #eee; color: #666; padding:0.2em; font- .notify-unseen { background-color: #cceeFF; } /* autocomplete popup */ - ul.acpopup { list-style: none; float: left; @@ -194,7 +193,6 @@ ul.acpopup { } nav .acpopup { width: 290px; - margin-left: -35px; max-height: 450px; max-width: 300px; overflow-y: auto; diff --git a/view/templates/message-head.tpl b/view/templates/message-head.tpl index 49f268c42..3fabebc0d 100644 --- a/view/templates/message-head.tpl +++ b/view/templates/message-head.tpl @@ -1,7 +1,7 @@ From 9fba5041a0ee79b551df81441fdac4f75bcaded2 Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Sat, 30 Jan 2016 16:42:46 +0100 Subject: [PATCH 06/17] rework autocomplete: update query-textcomplete --- .../jquery.textcomplete.js | 281 +++++++++++++----- 1 file changed, 202 insertions(+), 79 deletions(-) diff --git a/library/jquery-textcomplete/jquery.textcomplete.js b/library/jquery-textcomplete/jquery.textcomplete.js index 3df84f3b4..200310175 100644 --- a/library/jquery-textcomplete/jquery.textcomplete.js +++ b/library/jquery-textcomplete/jquery.textcomplete.js @@ -1,3 +1,16 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof module === "object" && module.exports) { + var $ = require('jquery'); + module.exports = factory($); + } else { + // Browser globals + factory(jQuery); + } +}(function (jQuery) { + /*! * jQuery.textcomplete * @@ -17,13 +30,18 @@ if (typeof jQuery === 'undefined') { if (console.warn) { console.warn(message); } }; + var id = 1; + $.fn.textcomplete = function (strategies, option) { var args = Array.prototype.slice.call(arguments); return this.each(function () { + var self = this; var $this = $(this); var completer = $this.data('textComplete'); if (!completer) { - completer = new $.fn.textcomplete.Completer(this, option || {}); + option || (option = {}); + option._oid = id++; // unique object id + completer = new $.fn.textcomplete.Completer(this, option); $this.data('textComplete', completer); } if (typeof strategies === 'string') { @@ -45,7 +63,10 @@ if (typeof jQuery === 'undefined') { } }); }); - completer.register($.fn.textcomplete.Strategy.parse(strategies)); + completer.register($.fn.textcomplete.Strategy.parse(strategies, { + el: self, + $el: $this + })); } }); }; @@ -115,6 +136,10 @@ if (typeof jQuery === 'undefined') { return Object.prototype.toString.call(obj) === '[object String]'; }; + var isFunction = function (obj) { + return Object.prototype.toString.call(obj) === '[object Function]'; + }; + var uniqueId = 0; function Completer(element, option) { @@ -224,8 +249,10 @@ if (typeof jQuery === 'undefined') { // // value - The selected element of the array callbacked from search func. // strategy - The Strategy object. - select: function (value, strategy) { - this.adapter.select(value, strategy); + // e - Click or keydown event object. + select: function (value, strategy, e) { + this._term = null; + this.adapter.select(value, strategy, e); this.fire('change').fire('textComplete:select', value, strategy); this.adapter.focus(); }, @@ -248,8 +275,9 @@ if (typeof jQuery === 'undefined') { var strategy = this.strategies[i]; var context = strategy.context(text); if (context || context === '') { + var matchRegexp = isFunction(strategy.match) ? strategy.match(text) : strategy.match; if (isString(context)) { text = context; } - var match = text.match(strategy.match); + var match = text.match(matchRegexp); if (match) { return [strategy, match[strategy.index], match]; } } } @@ -262,14 +290,14 @@ if (typeof jQuery === 'undefined') { strategy.search(term, function (data, stillSearching) { if (!self.dropdown.shown) { self.dropdown.activate(); - self.dropdown.setPosition(self.adapter.getCaretPosition()); } if (self._clearAtNext) { // The first callback in the current lock. self.dropdown.clear(); self._clearAtNext = false; } - self.dropdown.render(self._zip(data, strategy)); + self.dropdown.setPosition(self.adapter.getCaretPosition()); + self.dropdown.render(self._zip(data, strategy, term)); if (!stillSearching) { // The last callback in the current lock. free(); @@ -284,9 +312,9 @@ if (typeof jQuery === 'undefined') { // // this._zip(['a', 'b'], 's'); // //=> [{ value: 'a', strategy: 's' }, { value: 'b', strategy: 's' }] - _zip: function (data, strategy) { + _zip: function (data, strategy, term) { return $.map(data, function (value) { - return { value: value, strategy: strategy }; + return { value: value, strategy: strategy, term: term }; }); } }); @@ -297,6 +325,8 @@ if (typeof jQuery === 'undefined') { +function ($) { 'use strict'; + var $window = $(window); + var include = function (zippedData, datum) { var i, elem; var idProperty = datum.strategy.idProperty @@ -320,6 +350,16 @@ if (typeof jQuery === 'undefined') { }); }); + var commands = { + SKIP_DEFAULT: 0, + KEY_UP: 1, + KEY_DOWN: 2, + KEY_ENTER: 3, + KEY_PAGEUP: 4, + KEY_PAGEDOWN: 5, + KEY_ESCAPE: 6 + }; + // Dropdown view // ============= @@ -327,7 +367,7 @@ if (typeof jQuery === 'undefined') { // // element - Textarea or contenteditable element. function Dropdown(element, completer, option) { - this.$el = Dropdown.findOrCreateElement(option); + this.$el = Dropdown.createElement(option); this.completer = completer; this.id = completer.id + 'dropdown'; this._data = []; // zipped data. @@ -338,7 +378,7 @@ if (typeof jQuery === 'undefined') { if (option.listPosition) { this.setPosition = option.listPosition; } if (option.height) { this.$el.height(option.height); } var self = this; - $.each(['maxCount', 'placement', 'footer', 'header', 'className'], function (_i, name) { + $.each(['maxCount', 'placement', 'footer', 'header', 'noResultsMessage', 'className'], function (_i, name) { if (option[name] != null) { self[name] = option[name]; } }); this._bindEvents(element); @@ -349,18 +389,19 @@ if (typeof jQuery === 'undefined') { // Class methods // ------------- - findOrCreateElement: function (option) { + createElement: function (option) { var $parent = option.appendTo; if (!($parent instanceof $)) { $parent = $($parent); } - var $el = $parent.children('.dropdown-menu') - if (!$el.length) { - $el = $('').css({ + var $el = $('
      ') + .addClass('dropdown-menu textcomplete-dropdown') + .attr('id', 'textcomplete-dropdown-' + option._oid) + .css({ display: 'none', left: 0, position: 'absolute', zIndex: option.zIndex - }).appendTo($parent); - } + }) + .appendTo($parent); return $el; } }); @@ -403,23 +444,26 @@ if (typeof jQuery === 'undefined') { this._renderFooter(unzippedData); if (contentsHtml) { this._renderContents(contentsHtml); + this._fitToBottom(); this._activateIndexedItem(); } this._setScroll(); + } else if (this.noResultsMessage) { + this._renderNoResultsMessage(unzippedData); } else if (this.shown) { this.deactivate(); } }, - setPosition: function (position) { - this.$el.css(this._applyPlacement(position)); + setPosition: function (pos) { + this.$el.css(this._applyPlacement(pos)); // Make the dropdown fixed if the input is also fixed // This can't be done during init, as textcomplete may be used on multiple elements on the same page // Because the same dropdown is reused behind the scenes, we need to recheck every time the dropdown is showed var position = 'absolute'; // Check if input or one of its parents has positioning we need to care about - this.$inputEl.add(this.$inputEl.parents()).each(function() { + this.$inputEl.add(this.$inputEl.parents()).each(function() { if($(this).css('position') === 'absolute') // The element has absolute positioning, so it's all OK return false; if($(this).css('position') === 'fixed') { @@ -436,7 +480,7 @@ if (typeof jQuery === 'undefined') { this.$el.html(''); this.data = []; this._index = 0; - this._$header = this._$footer = null; + this._$header = this._$footer = this._$noResultsMessage = null; }, activate: function () { @@ -481,19 +525,25 @@ if (typeof jQuery === 'undefined') { return e.keyCode === 34; // PAGEDOWN }, + isEscape: function (e) { + return e.keyCode === 27; // ESCAPE + }, + // Private properties // ------------------ _data: null, // Currently shown zipped data. _index: null, _$header: null, + _$noResultsMessage: null, _$footer: null, // Private methods // --------------- _bindEvents: function () { - this.$el.on('mousedown.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)) + this.$el.on('mousedown.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)); + this.$el.on('touchstart.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)); this.$el.on('mouseover.' + this.id, '.textcomplete-item', $.proxy(this._onMouseover, this)); this.$inputEl.on('keydown.' + this.id, $.proxy(this._onKeydown, this)); }, @@ -506,11 +556,16 @@ if (typeof jQuery === 'undefined') { $el = $el.closest('.textcomplete-item'); } var datum = this.data[parseInt($el.data('index'), 10)]; - this.completer.select(datum.value, datum.strategy); + this.completer.select(datum.value, datum.strategy, e); var self = this; // Deactive at next tick to allow other event handlers to know whether // the dropdown has been shown or not. - setTimeout(function () { self.deactivate(); }, 0); + setTimeout(function () { + self.deactivate(); + if (e.type === 'touchstart') { + self.$inputEl.focus(); + } + }, 0); }, // Activate hovered item. @@ -526,21 +581,58 @@ if (typeof jQuery === 'undefined') { _onKeydown: function (e) { if (!this.shown) { return; } + + var command; + + if ($.isFunction(this.option.onKeydown)) { + command = this.option.onKeydown(e, commands); + } + + if (command == null) { + command = this._defaultKeydown(e); + } + + switch (command) { + case commands.KEY_UP: + e.preventDefault(); + this._up(); + break; + case commands.KEY_DOWN: + e.preventDefault(); + this._down(); + break; + case commands.KEY_ENTER: + e.preventDefault(); + this._enter(e); + break; + case commands.KEY_PAGEUP: + e.preventDefault(); + this._pageup(); + break; + case commands.KEY_PAGEDOWN: + e.preventDefault(); + this._pagedown(); + break; + case commands.KEY_ESCAPE: + e.preventDefault(); + this.deactivate(); + break; + } + }, + + _defaultKeydown: function (e) { if (this.isUp(e)) { - e.preventDefault(); - this._up(); + return commands.KEY_UP; } else if (this.isDown(e)) { - e.preventDefault(); - this._down(); + return commands.KEY_DOWN; } else if (this.isEnter(e)) { - e.preventDefault(); - this._enter(); + return commands.KEY_ENTER; } else if (this.isPageup(e)) { - e.preventDefault(); - this._pageup(); + return commands.KEY_PAGEUP; } else if (this.isPagedown(e)) { - e.preventDefault(); - this._pagedown(); + return commands.KEY_PAGEDOWN; + } else if (this.isEscape(e)) { + return commands.KEY_ESCAPE; } }, @@ -564,10 +656,10 @@ if (typeof jQuery === 'undefined') { this._setScroll(); }, - _enter: function () { + _enter: function (e) { var datum = this.data[parseInt(this._getActiveElement().data('index'), 10)]; - this.completer.select(datum.value, datum.strategy); - this._setScroll(); + this.completer.select(datum.value, datum.strategy, e); + this.deactivate(); }, _pageup: function () { @@ -630,7 +722,7 @@ if (typeof jQuery === 'undefined') { index = this.data.length; this.data.push(datum); html += '
    • '; - html += datum.strategy.template(datum.value); + html += datum.strategy.template(datum.value, datum.term); html += '
    • '; } return html; @@ -656,6 +748,16 @@ if (typeof jQuery === 'undefined') { } }, + _renderNoResultsMessage: function (unzippedData) { + if (this.noResultsMessage) { + if (!this._$noResultsMessage) { + this._$noResultsMessage = $('
    • ').appendTo(this.$el); + } + var html = $.isFunction(this.noResultsMessage) ? this.noResultsMessage(unzippedData) : this.noResultsMessage; + this._$noResultsMessage.html(html); + } + }, + _renderContents: function (html) { if (this._$footer) { this._$footer.before(html); @@ -664,7 +766,15 @@ if (typeof jQuery === 'undefined') { } }, - _applyPlacement: function (position) { + _fitToBottom: function() { + var windowScrollBottom = $window.scrollTop() + $window.height(); + var height = this.$el.height(); + if ((this.$el.position().top + height) > windowScrollBottom) { + this.$el.offset({top: windowScrollBottom - height}); + } + }, + + _applyPlacement: function (position) { // If the 'placement' option set to 'top', move the position above the element. if (this.placement.indexOf('top') !== -1) { // Overwrite the position object to set the 'bottom' property instead of the top. @@ -688,6 +798,7 @@ if (typeof jQuery === 'undefined') { }); $.fn.textcomplete.Dropdown = Dropdown; + $.extend($.fn.textcomplete, commands); }(jQuery); +function ($) { @@ -713,9 +824,12 @@ if (typeof jQuery === 'undefined') { if (this.cache) { this.search = memoize(this.search); } } - Strategy.parse = function (optionsArray) { - return $.map(optionsArray, function (options) { - return new Strategy(options); + Strategy.parse = function (strategiesArray, params) { + return $.map(strategiesArray, function (strategy) { + var strategyObj = new Strategy(strategy); + strategyObj.el = params.el; + strategyObj.$el = params.$el; + return strategyObj; }); }; @@ -848,6 +962,7 @@ if (typeof jQuery === 'undefined') { // Suppress searching if it returns true. _skipSearch: function (clickEvent) { switch (clickEvent.keyCode) { + case 13: // ENTER case 40: // DOWN case 38: // UP return true; @@ -894,17 +1009,19 @@ if (typeof jQuery === 'undefined') { // -------------- // Update the textarea with the given value and strategy. - select: function (value, strategy) { + select: function (value, strategy, e) { var pre = this.getTextFromHeadToCaret(); var post = this.el.value.substring(this.el.selectionEnd); - var newSubstr = strategy.replace(value); - if ($.isArray(newSubstr)) { - post = newSubstr[1] + post; - newSubstr = newSubstr[0]; + var newSubstr = strategy.replace(value, e); + if (typeof newSubstr !== 'undefined') { + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr); + this.$el.val(pre + post); + this.el.selectionStart = this.el.selectionEnd = pre.length; } - pre = pre.replace(strategy.match, newSubstr); - this.$el.val(pre + post); - this.el.selectionStart = this.el.selectionEnd = pre.length; }, // Private methods @@ -981,22 +1098,24 @@ if (typeof jQuery === 'undefined') { // Public methods // -------------- - select: function (value, strategy) { + select: function (value, strategy, e) { var pre = this.getTextFromHeadToCaret(); var post = this.el.value.substring(pre.length); - var newSubstr = strategy.replace(value); - if ($.isArray(newSubstr)) { - post = newSubstr[1] + post; - newSubstr = newSubstr[0]; + var newSubstr = strategy.replace(value, e); + if (typeof newSubstr !== 'undefined') { + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr); + this.$el.val(pre + post); + this.el.focus(); + var range = this.el.createTextRange(); + range.collapse(true); + range.moveEnd('character', pre.length); + range.moveStart('character', pre.length); + range.select(); } - pre = pre.replace(strategy.match, newSubstr); - this.$el.val(pre + post); - this.el.focus(); - var range = this.el.createTextRange(); - range.collapse(true); - range.moveEnd('character', pre.length); - range.moveStart('character', pre.length); - range.select(); }, getTextFromHeadToCaret: function () { @@ -1032,7 +1151,7 @@ if (typeof jQuery === 'undefined') { // Update the content with the given value and strategy. // When an dropdown item is selected, it is executed. - select: function (value, strategy) { + select: function (value, strategy, e) { var pre = this.getTextFromHeadToCaret(); var sel = window.getSelection() var range = sel.getRangeAt(0); @@ -1040,20 +1159,22 @@ if (typeof jQuery === 'undefined') { selection.selectNodeContents(range.startContainer); var content = selection.toString(); var post = content.substring(range.startOffset); - var newSubstr = strategy.replace(value); - if ($.isArray(newSubstr)) { - post = newSubstr[1] + post; - newSubstr = newSubstr[0]; + var newSubstr = strategy.replace(value, e); + if (typeof newSubstr !== 'undefined') { + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr); + range.selectNodeContents(range.startContainer); + range.deleteContents(); + var node = document.createTextNode(pre + post); + range.insertNode(node); + range.setStart(node, pre.length); + range.collapse(true); + sel.removeAllRanges(); + sel.addRange(range); } - pre = pre.replace(strategy.match, newSubstr); - range.selectNodeContents(range.startContainer); - range.deleteContents(); - var node = document.createTextNode(pre + post); - range.insertNode(node); - range.setStart(node, pre.length); - range.collapse(true); - sel.removeAllRanges(); - sel.addRange(range); }, // Private methods @@ -1079,8 +1200,7 @@ if (typeof jQuery === 'undefined') { position.left -= this.$el.offset().left; position.top += $node.height() - this.$el.offset().top; position.lineHeight = $node.height(); - var dir = this.$el.attr('dir') || this.$el.css('direction'); - if (dir === 'rtl') { position.left -= this.listView.$el.width(); } + $node.remove(); return position; }, @@ -1102,3 +1222,6 @@ if (typeof jQuery === 'undefined') { $.fn.textcomplete.ContentEditable = ContentEditable; }(jQuery); + +return jQuery; +})); From 5f7c5e6ab66ff0340a33ea1f2f8b02ee83991d2d Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Mon, 1 Feb 2016 17:43:12 +0100 Subject: [PATCH 07/17] rework autocomplete: Access list of smilies over JSON --- include/network.php | 15 ++++ include/smilies.php | 179 ++++++++++++++++++++++++++++++++++++++++++++ include/text.php | 157 +------------------------------------- mod/smilies.php | 20 ++++- 4 files changed, 215 insertions(+), 156 deletions(-) create mode 100644 include/smilies.php diff --git a/include/network.php b/include/network.php index c6379e407..84f1297e0 100644 --- a/include/network.php +++ b/include/network.php @@ -1344,3 +1344,18 @@ function short_link($url) { } return $slinky->short(); }}; + +/** + * @brief Encodes content to json + * + * This function encodes an array to json format + * and adds an application/json HTTP header to the output. + * After finishing the process is getting killed. + * + * @param array $x + */ +function json_return_and_die($x) { + header("content-type: application/json"); + echo json_encode($x); + killme(); +} diff --git a/include/smilies.php b/include/smilies.php new file mode 100644 index 000000000..7b3feefca --- /dev/null +++ b/include/smilies.php @@ -0,0 +1,179 @@ + smilie shortcut + * 'icons' => icon in html + * + * @hook smilie ('texts' => smilies texts array, 'icons' => smilies html array) + */ + public static function list_smilies() { + + $texts = array( + '<3', + '</3', + '<\\3', + ':-)', + ';-)', + ':-(', + ':-P', + ':-p', + ':-"', + ':-"', + ':-x', + ':-X', + ':-D', + '8-|', + '8-O', + ':-O', + '\\o/', + 'o.O', + 'O.o', + 'o_O', + 'O_o', + ":'(", + ":-!", + ":-/", + ":-[", + "8-)", + ':beer', + ':homebrew', + ':coffee', + ':facepalm', + ':like', + ':dislike', + '~friendica', + 'red#', + 'red#matrix' + + ); + + $icons = array( + '<3', + '</3', + '<\\3', + ':-)', + ';-)', + ':-(', + ':-P', + ':-p', + ':-\', + ':-\', + ':-x', + ':-X', + ':-D', + '8-|', + '8-O', + ':-O', + '\\o/', + 'o.O', + 'O.o', + 'o_O', + 'O_o', + ':\'(', + ':-!', + ':-/', + ':-[', + '8-)', + ':beer', + ':homebrew', + ':coffee', + ':facepalm', + ':like', + ':dislike', + '~friendica ~friendica', + 'redredmatrix', + 'redredmatrix' + ); + + $params = array('texts' => $texts, 'icons' => $icons); + call_hooks('smilie', $params); + + return $params; + + } + + /** + * @brief Replaces text emoticons with graphical images + * + * It is expected that this function will be called using HTML text. + * We will escape text between HTML pre and code blocks from being + * processed. + * + * At a higher level, the bbcode [nosmile] tag can be used to prevent this + * function from being executed by the prepare_text() routine when preparing + * bbcode source for HTML display + * + * @param string $s + * @param boolean $sample + * + * @return string + */ + public static function replace($s, $sample = false) { + if(intval(get_config('system','no_smilies')) + || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies')))) + return $s; + + $s = preg_replace_callback('/
      (.*?)<\/pre>/ism','self::smile_encode',$s);
      +		$s = preg_replace_callback('/(.*?)<\/code>/ism','self::smile_encode',$s);
      +
      +		$params = self::list_smilies();
      +		$params['string'] = $s;
      +
      +		if($sample) {
      +			$s = '
      '; + for($x = 0; $x < count($params['texts']); $x ++) { + $s .= '
      ' . $params['texts'][$x] . '
      ' . $params['icons'][$x] . '
      '; + } + } + else { + $params['string'] = preg_replace_callback('/<(3+)/','self::preg_heart',$params['string']); + $s = str_replace($params['texts'],$params['icons'],$params['string']); + } + + $s = preg_replace_callback('/
      (.*?)<\/pre>/ism','self::smile_decode',$s);
      +		$s = preg_replace_callback('/(.*?)<\/code>/ism','self::smile_decode',$s);
      +
      +		return $s;
      +	}
      +
      +	private function smile_encode($m) {
      +		return(str_replace($m[1],base64url_encode($m[1]),$m[0]));
      +	}
      +
      +	private function smile_decode($m) {
      +		return(str_replace($m[1],base64url_decode($m[1]),$m[0]));
      +	}
      +
      +
      +	/**
      +	 * @brief expand <3333 to the correct number of hearts
      +	 *
      +	 * @param string $x
      +	 * @return string
      +	 */
      +	private function preg_heart($x) {
      +		if(strlen($x[1]) == 1)
      +			return $x[0];
      +		$t = '';
      +		for($cnt = 0; $cnt < strlen($x[1]); $cnt ++)
      +			$t .= '<3';
      +		$r =  str_replace($x[0],$t,$x[0]);
      +		return $r;
      +	}
      +
      +}
      diff --git a/include/text.php b/include/text.php
      index 4f3af5aee..eb8dde40c 100644
      --- a/include/text.php
      +++ b/include/text.php
      @@ -2,6 +2,7 @@
       
       require_once("include/template_processor.php");
       require_once("include/friendica_smarty.php");
      +require_once("include/smilies.php");
       require_once("include/map.php");
       require_once("mod/proxy.php");
       
      @@ -1079,160 +1080,6 @@ function get_mood_verbs() {
       	return $arr;
       }
       
      -
      -
      -if(! function_exists('smilies')) {
      -/**
      - * Replaces text emoticons with graphical images
      - *
      - * It is expected that this function will be called using HTML text.
      - * We will escape text between HTML pre and code blocks from being
      - * processed.
      - *
      - * At a higher level, the bbcode [nosmile] tag can be used to prevent this
      - * function from being executed by the prepare_text() routine when preparing
      - * bbcode source for HTML display
      - *
      - * @param string $s
      - * @param boolean $sample
      - * @return string
      - * @hook smilie ('texts' => smilies texts array, 'icons' => smilies html array, 'string' => $s)
      - */
      -function smilies($s, $sample = false) {
      -	$a = get_app();
      -
      -	if(intval(get_config('system','no_smilies'))
      -		|| (local_user() && intval(get_pconfig(local_user(),'system','no_smilies'))))
      -		return $s;
      -
      -	$s = preg_replace_callback('/
      (.*?)<\/pre>/ism','smile_encode',$s);
      -	$s = preg_replace_callback('/(.*?)<\/code>/ism','smile_encode',$s);
      -
      -	$texts =  array(
      -		'<3',
      -		'</3',
      -		'<\\3',
      -		':-)',
      -		';-)',
      -		':-(',
      -		':-P',
      -		':-p',
      -		':-"',
      -		':-"',
      -		':-x',
      -		':-X',
      -		':-D',
      -		'8-|',
      -		'8-O',
      -		':-O',
      -		'\\o/',
      -		'o.O',
      -		'O.o',
      -		'o_O',
      -		'O_o',
      -		":'(",
      -		":-!",
      -		":-/",
      -		":-[",
      -		"8-)",
      -		':beer',
      -		':homebrew',
      -		':coffee',
      -		':facepalm',
      -		':like',
      -		':dislike',
      -		'~friendica',
      -		'red#',
      -		'red#matrix'
      -
      -	);
      -
      -	$icons = array(
      -		'<3',
      -		'</3',
      -		'<\\3',
      -		':-)',
      -		';-)',
      -		':-(',
      -		':-P',
      -		':-p',
      -		':-\',
      -		':-\',
      -		':-x',
      -		':-X',
      -		':-D',
      -		'8-|',
      -		'8-O',
      -		':-O',
      -		'\\o/',
      -		'o.O',
      -		'O.o',
      -		'o_O',
      -		'O_o',
      -		':\'(',
      -		':-!',
      -		':-/',
      -		':-[',
      -		'8-)',
      -		':beer',
      -		':homebrew',
      -		':coffee',
      -		':facepalm',
      -		':like',
      -		':dislike',
      -		'~friendica ~friendica',
      -		'redredmatrix',
      -		'redredmatrix'
      -	);
      -
      -	$params = array('texts' => $texts, 'icons' => $icons, 'string' => $s);
      -	call_hooks('smilie', $params);
      -
      -	if($sample) {
      -		$s = '
      '; - for($x = 0; $x < count($params['texts']); $x ++) { - $s .= '
      ' . $params['texts'][$x] . '
      ' . $params['icons'][$x] . '
      '; - } - } - else { - $params['string'] = preg_replace_callback('/<(3+)/','preg_heart',$params['string']); - $s = str_replace($params['texts'],$params['icons'],$params['string']); - } - - $s = preg_replace_callback('/
      (.*?)<\/pre>/ism','smile_decode',$s);
      -	$s = preg_replace_callback('/(.*?)<\/code>/ism','smile_decode',$s);
      -
      -	return $s;
      -
      -}}
      -
      -function smile_encode($m) {
      -	return(str_replace($m[1],base64url_encode($m[1]),$m[0]));
      -}
      -
      -function smile_decode($m) {
      -	return(str_replace($m[1],base64url_decode($m[1]),$m[0]));
      -}
      -
      -
      -/**
      - * expand <3333 to the correct number of hearts
      - *
      - * @param string $x
      - * @return string
      - */
      -function preg_heart($x) {
      -	$a = get_app();
      -	if(strlen($x[1]) == 1)
      -		return $x[0];
      -	$t = '';
      -	for($cnt = 0; $cnt < strlen($x[1]); $cnt ++)
      -		$t .= '<3';
      -	$r =  str_replace($x[0],$t,$x[0]);
      -	return $r;
      -}
      -
      -
       if(! function_exists('day_translate')) {
       /**
        * Translate days and months names
      @@ -1549,7 +1396,7 @@ function prepare_text($text) {
       	if(stristr($text,'[nosmile]'))
       		$s = bbcode($text);
       	else
      -		$s = smilies(bbcode($text));
      +		$s = smilies::replace(bbcode($text));
       
       	return trim($s);
       }}
      diff --git a/mod/smilies.php b/mod/smilies.php
      index c47f95da7..5a157e368 100644
      --- a/mod/smilies.php
      +++ b/mod/smilies.php
      @@ -1,3 +1,21 @@
       argv[1]==="json"){
      +		$tmp = smilies::list_smilies();
      +		$results = array();
      +		for($i = 0; $i < count($tmp['texts']); $i++) {
      +			$results[] = array('text' => $tmp['texts'][$i], 'icon' => $tmp['icons'][$i]);
      +		}
      +		json_return_and_die($results);
      +	}
      +	else {
      +		return smilies('',true);
      +	}
      +}
      
      From aee94b38c174f0e182d6d14309ef3f4aef2563b0 Mon Sep 17 00:00:00 2001
      From: rabuzarus <>
      Date: Mon, 1 Feb 2016 18:21:29 +0100
      Subject: [PATCH 08/17] rework autocomplete: add class dir and static function
      
      ---
       include/acl_selectors.php        |  4 +-
       include/dir_fns.php              | 74 +++++++++++++++++---------------
       view/templates/contacts-head.tpl | 12 ------
       3 files changed, 42 insertions(+), 48 deletions(-)
      
      diff --git a/include/acl_selectors.php b/include/acl_selectors.php
      index 46f737e4c..b5c43e38e 100644
      --- a/include/acl_selectors.php
      +++ b/include/acl_selectors.php
      @@ -659,7 +659,7 @@ function acl_lookup(&$a, $out_type = 'json') {
        * @brief Searching for global contacts for autocompletion
        * 
        * @param App $a
      - * @return type
      + * @return array
        */
       function navbar_complete(&$a) {
       
      @@ -682,7 +682,7 @@ function navbar_complete(&$a) {
       		$search = substr($search,1);
       
       	if($localsearch) {
      -		$x = dirsearch_global_by_name($search);
      +		$x = dir::global_search_by_name($search);
       		return $x;
       	}
       
      diff --git a/include/dir_fns.php b/include/dir_fns.php
      index 2e39e7ddb..b85cb5b2d 100644
      --- a/include/dir_fns.php
      +++ b/include/dir_fns.php
      @@ -2,45 +2,51 @@
       
       /**
        * @file include/dir_fns.php
      - * @brief Functions for directory
        */
       
      +
       /**
      - * @brief Search global contact table by nick or name
      - *  * 
      - * @param string $search
      - * @return array
      + * @brief This class handels directory related functions
        */
      -function dirsearch_global_by_name($search) {
      +class dir {
       
      -	if($search) {
      -		// check supported networks
      -		if (get_config('system','diaspora_enabled'))
      -			$diaspora = NETWORK_DIASPORA;
      -		else
      -			$diaspora = NETWORK_DFRN;
      +	/**
      +	 * @brief Search global contact table by nick or name
      +	 *  * 
      +	 * @param string $search Name or nick
      +	 * @return array
      +	 */
      +	public static function global_search_by_name($search) {
       
      -		if (!get_config('system','ostatus_disabled'))
      -			$ostatus = NETWORK_OSTATUS;
      -		else
      -			$ostatus = NETWORK_DFRN;
      +		if($search) {
      +			// check supported networks
      +			if (get_config('system','diaspora_enabled'))
      +				$diaspora = NETWORK_DIASPORA;
      +			else
      +				$diaspora = NETWORK_DFRN;
      +
      +			if (!get_config('system','ostatus_disabled'))
      +				$ostatus = NETWORK_OSTATUS;
      +			else
      +				$ostatus = NETWORK_DFRN;
      +
      +			$results = q("SELECT `contact`.`id` AS `cid`, `gcontact`.`url`, `gcontact`.`name`, `gcontact`.`nick`, `gcontact`.`photo`,
      +							`gcontact`.`network`, `gcontact`.`keywords`, `gcontact`.`addr`
      +						FROM `gcontact`
      +						LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl`
      +							AND `contact`.`uid` = %d AND NOT `contact`.`blocked`
      +							AND NOT `contact`.`pending` AND `contact`.`rel` IN ('%s', '%s')
      +						WHERE (`contact`.`id` > 0 OR (NOT `gcontact`.`hide` AND `gcontact`.`network` IN ('%s', '%s', '%s') AND
      +						((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)))) AND
      +						(`gcontact`.`url` REGEXP '%s' OR `gcontact`.`name` REGEXP '%s' OR `gcontact`.`nick` REGEXP '%s'
      +							) 
      +							GROUP BY `gcontact`.`nurl`
      +							ORDER BY `gcontact`.`updated` DESC ",
      +						intval(local_user()), dbesc(CONTACT_IS_SHARING), dbesc(CONTACT_IS_FRIEND),
      +						dbesc(NETWORK_DFRN), dbesc($ostatus), dbesc($diaspora),
      +						dbesc(escape_tags($search)), dbesc(escape_tags($search)), dbesc(escape_tags($search)));
      +			return $results;
      +		}
       
      -		$results = q("SELECT `contact`.`id` AS `cid`, `gcontact`.`url`, `gcontact`.`name`, `gcontact`.`nick`, `gcontact`.`photo`,
      -						`gcontact`.`network`, `gcontact`.`keywords`, `gcontact`.`addr`
      -					FROM `gcontact`
      -					LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl`
      -						AND `contact`.`uid` = %d AND NOT `contact`.`blocked`
      -						AND NOT `contact`.`pending` AND `contact`.`rel` IN ('%s', '%s')
      -					WHERE (`contact`.`id` > 0 OR (NOT `gcontact`.`hide` AND `gcontact`.`network` IN ('%s', '%s', '%s') AND
      -					((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)))) AND
      -					(`gcontact`.`url` REGEXP '%s' OR `gcontact`.`name` REGEXP '%s' OR `gcontact`.`nick` REGEXP '%s'
      -						) 
      -						GROUP BY `gcontact`.`nurl`
      -						ORDER BY `gcontact`.`updated` DESC ",
      -					intval(local_user()), dbesc(CONTACT_IS_SHARING), dbesc(CONTACT_IS_FRIEND),
      -					dbesc(NETWORK_DFRN), dbesc($ostatus), dbesc($diaspora),
      -					dbesc(escape_tags($search)), dbesc(escape_tags($search)), dbesc(escape_tags($search)));
      -		return $results;
       	}
      -		
      -}
      \ No newline at end of file
      +}
      diff --git a/view/templates/contacts-head.tpl b/view/templates/contacts-head.tpl
      index 25055d405..ea562233f 100644
      --- a/view/templates/contacts-head.tpl
      +++ b/view/templates/contacts-head.tpl
      @@ -2,18 +2,6 @@
       
       
      
      From 00e319faa269801e9ffe1f60ad1b4d74946522c7 Mon Sep 17 00:00:00 2001
      From: rabuzarus <>
      Date: Mon, 1 Feb 2016 18:22:26 +0100
      Subject: [PATCH 09/17] rework autocomplete: add bbcode autocompletion to
       editor
      
      ---
       js/autocomplete.js | 25 +++++++++++++++++++++----
       1 file changed, 21 insertions(+), 4 deletions(-)
      
      diff --git a/js/autocomplete.js b/js/autocomplete.js
      index 1f7df011d..6c75f17ca 100644
      --- a/js/autocomplete.js
      +++ b/js/autocomplete.js
      @@ -1,8 +1,15 @@
       /**
      - * Friendica people autocomplete
      + * @brief Friendica people autocomplete
        *
        * require jQuery, jquery.textcomplete
      + * 
      + * 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
        */
      +
      +
       function contact_search(term, callback, backend_url, type) {
       
       	// Check if there is a conversation id to include the unkonwn contacts of the conversation
      @@ -107,6 +114,9 @@ function submit_form(e) {
       (function( $ ) {
       	$.fn.editor_autocomplete = function(backend_url) {
       
      +		// 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' ];
      +
       		// Autocomplete contacts
       		contacts = {
       			match: /(^|\s)(@\!*)([^ \n]+)$/,
      @@ -119,12 +129,19 @@ function submit_form(e) {
       		smilies = {
       			match: /(^|\s)(:[a-z]{2,})$/,
       			index: 2,
      -			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; },
      +			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; },
       			replace: function(item) { return "$1" + item.text + ' '; },
       		};
      +
      +		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 + ']']; },
      +		};
       		this.attr('autocomplete','off');
      -		this.textcomplete([contacts,smilies], {className:'acpopup', zIndex:1020});
      +		this.textcomplete([contacts,smilies, bbtags], {className:'acpopup', zIndex:1020});
       	};
       })( jQuery );
       
      
      From aff775e55c51fa2745656bb2e2607f1c5710e56f Mon Sep 17 00:00:00 2001
      From: rabuzarus <>
      Date: Tue, 2 Feb 2016 22:33:14 +0100
      Subject: [PATCH 10/17] rework autocomplete: add NavBar forum search
      
      ---
       include/acl_selectors.php | 14 ++++++++------
       include/dir_fns.php       | 14 ++++++++++----
       js/autocomplete.js        | 21 +++++++++++++++++----
       3 files changed, 35 insertions(+), 14 deletions(-)
      
      diff --git a/include/acl_selectors.php b/include/acl_selectors.php
      index b5c43e38e..9e1a2642a 100644
      --- a/include/acl_selectors.php
      +++ b/include/acl_selectors.php
      @@ -395,11 +395,12 @@ function acl_lookup(&$a, $out_type = 'json') {
       	if(!local_user())
       		return "";
       
      -	$start = (x($_REQUEST,'start')?$_REQUEST['start']:0);
      -	$count = (x($_REQUEST,'count')?$_REQUEST['count']:100);
      -	$search = (x($_REQUEST,'search')?$_REQUEST['search']:"");
      -	$type = (x($_REQUEST,'type')?$_REQUEST['type']:"");
      -	$conv_id = (x($_REQUEST,'conversation')?$_REQUEST['conversation']:null);
      +	$start	=	(x($_REQUEST,'start')		? $_REQUEST['start']		: 0);
      +	$count	=	(x($_REQUEST,'count')		? $_REQUEST['count']		: 100);
      +	$search	 =	(x($_REQUEST,'search')		? $_REQUEST['search']		: "");
      +	$type	=	(x($_REQUEST,'type')		? $_REQUEST['type']		: "");
      +	$mode	=	(x($_REQUEST,'mode')		? $_REQUEST['mode']		: "");
      +	$conv_id =	(x($_REQUEST,'conversation')	? $_REQUEST['conversation']	: null);
       
       	// For use with jquery.textcomplete for private mail completion
       
      @@ -673,6 +674,7 @@ function navbar_complete(&$a) {
       	$localsearch = get_config('system','poco_local_search');
       
       	$search = $prefix.notags(trim($_REQUEST['search']));
      +	$mode = $_REQUEST['mode'];
       
       	// don't search if search term has less than 2 characters
       	if(! $search || mb_strlen($search) < 2)
      @@ -682,7 +684,7 @@ function navbar_complete(&$a) {
       		$search = substr($search,1);
       
       	if($localsearch) {
      -		$x = dir::global_search_by_name($search);
      +		$x = dir::global_search_by_name($search, $mode);
       		return $x;
       	}
       
      diff --git a/include/dir_fns.php b/include/dir_fns.php
      index b85cb5b2d..d25805876 100644
      --- a/include/dir_fns.php
      +++ b/include/dir_fns.php
      @@ -14,9 +14,10 @@ class dir {
       	 * @brief Search global contact table by nick or name
       	 *  * 
       	 * @param string $search Name or nick
      +	 * @param string $mode Search mode 
       	 * @return array
       	 */
      -	public static function global_search_by_name($search) {
      +	public static function global_search_by_name($search, $mode = '') {
       
       		if($search) {
       			// check supported networks
      @@ -30,6 +31,12 @@ class dir {
       			else
       				$ostatus = NETWORK_DFRN;
       
      +			// check if fo
      +			if($mode === "community")
      +				$extra_sql = " AND `community`";
      +			else
      +				$extra_sql = "";
      +
       			$results = q("SELECT `contact`.`id` AS `cid`, `gcontact`.`url`, `gcontact`.`name`, `gcontact`.`nick`, `gcontact`.`photo`,
       							`gcontact`.`network`, `gcontact`.`keywords`, `gcontact`.`addr`
       						FROM `gcontact`
      @@ -38,13 +45,12 @@ class dir {
       							AND NOT `contact`.`pending` AND `contact`.`rel` IN ('%s', '%s')
       						WHERE (`contact`.`id` > 0 OR (NOT `gcontact`.`hide` AND `gcontact`.`network` IN ('%s', '%s', '%s') AND
       						((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)))) AND
      -						(`gcontact`.`url` REGEXP '%s' OR `gcontact`.`name` REGEXP '%s' OR `gcontact`.`nick` REGEXP '%s'
      -							) 
      +						(`gcontact`.`name` REGEXP '%s' OR `gcontact`.`nick` REGEXP '%s') $extra_sql
       							GROUP BY `gcontact`.`nurl`
       							ORDER BY `gcontact`.`updated` DESC ",
       						intval(local_user()), dbesc(CONTACT_IS_SHARING), dbesc(CONTACT_IS_FRIEND),
       						dbesc(NETWORK_DFRN), dbesc($ostatus), dbesc($diaspora),
      -						dbesc(escape_tags($search)), dbesc(escape_tags($search)), dbesc(escape_tags($search)));
      +						dbesc(escape_tags($search)), dbesc(escape_tags($search)));
       			return $results;
       		}
       
      diff --git a/js/autocomplete.js b/js/autocomplete.js
      index 6c75f17ca..aa8b6836c 100644
      --- a/js/autocomplete.js
      +++ b/js/autocomplete.js
      @@ -10,12 +10,11 @@
        */
       
       
      -function contact_search(term, callback, backend_url, type) {
      +function contact_search(term, callback, backend_url, type, mode) {
       
       	// Check if there is a conversation id to include the unkonwn contacts of the conversation
       	var conv_id = document.activeElement.id.match(/\d+$/);
       
      -
       	// 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] = {};
      @@ -41,6 +40,9 @@ function contact_search(term, callback, backend_url, type) {
       	if(conv_id !== null)
       		postdata['conversation'] = conv_id[0];
       
      +	if(mode !== null)
      +		postdata['mode'] = mode;
      +
       
       	$.ajax({
       		type:'POST',
      @@ -126,6 +128,7 @@ function submit_form(e) {
       			template: contact_format,
       		};
       
      +		// Autocomplete smilies e.g. ":like"
       		smilies = {
       			match: /(^|\s)(:[a-z]{2,})$/,
       			index: 2,
      @@ -134,6 +137,7 @@ function submit_form(e) {
       			replace: function(item) { return "$1" + item.text + ' '; },
       		};
       
      +		// Autocomplete BBTags
       		bbtags = {
       			match: /\[(\w*)$/,
       			index: 1,
      @@ -154,12 +158,21 @@ function submit_form(e) {
       		contacts = {
       			match: /(^@)([^\n]{2,})$/,
       			index: 2,
      -			search: function(term, callback) { contact_search(term, callback, backend_url, 'x'); },
      +			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'); },
       			replace: basic_replace,
       			template: contact_format,
       		};
       		this.attr('autocomplete', 'off');
      -		var a = this.textcomplete([contacts], {className:'acpopup', maxCount:100, zIndex: 1020, appendTo:'nav'});
      +		var a = this.textcomplete([contacts, community], {className:'acpopup', maxCount:100, zIndex: 1020, appendTo:'#nav-search-box'});
       		a.on('textComplete:select', function(e, value, strategy) { submit_form(this); });
       	};
       })( jQuery );
      
      From ad5be2901e131ec28356c7bdbb3cd0f748ffaa54 Mon Sep 17 00:00:00 2001
      From: rabuzarus <>
      Date: Wed, 3 Feb 2016 00:25:33 +0100
      Subject: [PATCH 11/17] rework autocomplete: some styling if contact is forum
      
      ---
       include/acl_selectors.php | 11 +++++++----
       include/dir_fns.php       |  2 +-
       js/autocomplete.js        |  5 +++--
       view/theme/vier/style.css | 11 +++++++++++
       4 files changed, 22 insertions(+), 7 deletions(-)
      
      diff --git a/include/acl_selectors.php b/include/acl_selectors.php
      index 9e1a2642a..99848aa19 100644
      --- a/include/acl_selectors.php
      +++ b/include/acl_selectors.php
      @@ -509,7 +509,7 @@ function acl_lookup(&$a, $out_type = 'json') {
       
       	if ($type==''){
       
      -		$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum` FROM `contact`
      +		$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum`, `prv` FROM `contact`
       			WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0 AND `notify` != ''
       			AND NOT (`network` IN ('%s', '%s'))
       			$sql_extra2
      @@ -520,7 +520,7 @@ function acl_lookup(&$a, $out_type = 'json') {
       	}
       	elseif ($type=='c'){
       
      -		$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum` FROM `contact`
      +		$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum`, `prv` FROM `contact`
       			WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0 AND `notify` != ''
       			AND NOT (`network` IN ('%s'))
       			$sql_extra2
      @@ -542,7 +542,7 @@ function acl_lookup(&$a, $out_type = 'json') {
       		);
       	}
       	elseif($type == 'a') {
      -		$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag` FROM `contact`
      +		$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum`, `prv` FROM `contact`
       			WHERE `uid` = %d AND `pending` = 0
       			$sql_extra2
       			ORDER BY `name` ASC ",
      @@ -559,6 +559,9 @@ function acl_lookup(&$a, $out_type = 'json') {
       					"photo"    => $g['photo'],
       					"name"     => $g['name'],
       					"nick"     => (x($g['addr']) ? $g['addr'] : $g['url']),
      +					"network" => $g['network'],
      +					"link" => $g['url'],
      +					"forum"	   => (x($g['community']) ? 1 : 0),
       				);
       			}
       		}
      @@ -584,7 +587,7 @@ function acl_lookup(&$a, $out_type = 'json') {
       				"network" => $g['network'],
       				"link" => $g['url'],
       				"nick" => htmlentities(($g['attag']) ? $g['attag'] : $g['nick']),
      -				"forum" => $g['forum']
      +				"forum" => ((x($g['forum']) || x($g['prv'])) ? 1 : 0),
       			);
       		}
       	}
      diff --git a/include/dir_fns.php b/include/dir_fns.php
      index d25805876..5f018ed8c 100644
      --- a/include/dir_fns.php
      +++ b/include/dir_fns.php
      @@ -38,7 +38,7 @@ class dir {
       				$extra_sql = "";
       
       			$results = q("SELECT `contact`.`id` AS `cid`, `gcontact`.`url`, `gcontact`.`name`, `gcontact`.`nick`, `gcontact`.`photo`,
      -							`gcontact`.`network`, `gcontact`.`keywords`, `gcontact`.`addr`
      +							`gcontact`.`network`, `gcontact`.`keywords`, `gcontact`.`addr`, `gcontact`.`community`
       						FROM `gcontact`
       						LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl`
       							AND `contact`.`uid` = %d AND NOT `contact`.`blocked`
      diff --git a/js/autocomplete.js b/js/autocomplete.js
      index aa8b6836c..f31161ff8 100644
      --- a/js/autocomplete.js
      +++ b/js/autocomplete.js
      @@ -24,7 +24,7 @@ function contact_search(term, callback, backend_url, type, mode) {
       		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
      -			matching.unshift({taggable:false, text: term, replace: term});
      +			matching.unshift({forum:false, text: term, replace: term});
       			setTimeout(function() { callback(matching); } , 1); // Use "pseudo-thread" to avoid some problems
       			return;
       		}
      @@ -68,9 +68,10 @@ 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);
      +		var forum = ((item.forum) ? 'forum' : '');
       		if(typeof desc === 'undefined') desc = '';
       		if(desc) desc = ' ('+desc+')';
      -		return "
      {2}{3}
      ".format(item.taggable, item.photo, item.name, desc, item.link); + return "
      {2}{3}
      ".format(forum, item.photo, item.name, desc, item.link); } else return "
      " + item.text + "
      "; diff --git a/view/theme/vier/style.css b/view/theme/vier/style.css index a7b15c581..7a73e03e8 100644 --- a/view/theme/vier/style.css +++ b/view/theme/vier/style.css @@ -935,8 +935,19 @@ nav .acpopup { color: #737373; } .textcomplete-item a:hover { + color: #000; padding: 3px 20px; } +.textcomplete-item a .forum, .forum .acpopup-sub-text { + color: #36C; + opacity: 0.8; +} +.textcomplete-item a .forum:hover { + opacity: 1.0; +} +img.acpopup-img { + border-radius: 4px; +} #nav-notifications-menu { width: 400px; From 30c375d238af79795937b8df046abe9f0ee63db9 Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Fri, 5 Feb 2016 14:26:22 +0100 Subject: [PATCH 12/17] rework autocomplete: polishing class and methods naming --- include/{dir_fns.php => DirSearch.php} | 4 ++-- include/{smilies.php => Smilies.php} | 18 +++++++++--------- include/acl_selectors.php | 4 ++-- include/text.php | 4 ++-- mod/smilies.php | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) rename include/{dir_fns.php => DirSearch.php} (97%) rename include/{smilies.php => Smilies.php} (92%) diff --git a/include/dir_fns.php b/include/DirSearch.php similarity index 97% rename from include/dir_fns.php rename to include/DirSearch.php index 5f018ed8c..a6a0b59fc 100644 --- a/include/dir_fns.php +++ b/include/DirSearch.php @@ -1,14 +1,14 @@ smilies texts array, 'icons' => smilies html array) */ - public static function list_smilies() { + public static function get_list() { $texts = array( '<3', @@ -128,10 +128,10 @@ class smilies { || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies')))) return $s; - $s = preg_replace_callback('/
      (.*?)<\/pre>/ism','self::smile_encode',$s);
      -		$s = preg_replace_callback('/(.*?)<\/code>/ism','self::smile_encode',$s);
      +		$s = preg_replace_callback('/
      (.*?)<\/pre>/ism','self::encode',$s);
      +		$s = preg_replace_callback('/(.*?)<\/code>/ism','self::encode',$s);
       
      -		$params = self::list_smilies();
      +		$params = self::get_list();
       		$params['string'] = $s;
       
       		if($sample) {
      @@ -145,17 +145,17 @@ class smilies {
       			$s = str_replace($params['texts'],$params['icons'],$params['string']);
       		}
       
      -		$s = preg_replace_callback('/
      (.*?)<\/pre>/ism','self::smile_decode',$s);
      -		$s = preg_replace_callback('/(.*?)<\/code>/ism','self::smile_decode',$s);
      +		$s = preg_replace_callback('/
      (.*?)<\/pre>/ism','self::decode',$s);
      +		$s = preg_replace_callback('/(.*?)<\/code>/ism','self::decode',$s);
       
       		return $s;
       	}
       
      -	private function smile_encode($m) {
      +	private function encode($m) {
       		return(str_replace($m[1],base64url_encode($m[1]),$m[0]));
       	}
       
      -	private function smile_decode($m) {
      +	private function decode($m) {
       		return(str_replace($m[1],base64url_decode($m[1]),$m[0]));
       	}
       
      diff --git a/include/acl_selectors.php b/include/acl_selectors.php
      index 99848aa19..4a35fd0f0 100644
      --- a/include/acl_selectors.php
      +++ b/include/acl_selectors.php
      @@ -6,7 +6,7 @@
       
       require_once("include/contact_selectors.php");
       require_once("include/contact_widgets.php");
      -require_once("include/dir_fns.php");
      +require_once("include/DirSearch.php");
       require_once("include/features.php");
       require_once("mod/proxy.php");
       
      @@ -687,7 +687,7 @@ function navbar_complete(&$a) {
       		$search = substr($search,1);
       
       	if($localsearch) {
      -		$x = dir::global_search_by_name($search, $mode);
      +		$x = DirSearch::global_search_by_name($search, $mode);
       		return $x;
       	}
       
      diff --git a/include/text.php b/include/text.php
      index eb8dde40c..e14930b10 100644
      --- a/include/text.php
      +++ b/include/text.php
      @@ -2,7 +2,7 @@
       
       require_once("include/template_processor.php");
       require_once("include/friendica_smarty.php");
      -require_once("include/smilies.php");
      +require_once("include/Smilies.php");
       require_once("include/map.php");
       require_once("mod/proxy.php");
       
      @@ -1396,7 +1396,7 @@ function prepare_text($text) {
       	if(stristr($text,'[nosmile]'))
       		$s = bbcode($text);
       	else
      -		$s = smilies::replace(bbcode($text));
      +		$s = Smilies::replace(bbcode($text));
       
       	return trim($s);
       }}
      diff --git a/mod/smilies.php b/mod/smilies.php
      index 5a157e368..b3d86cb3f 100644
      --- a/mod/smilies.php
      +++ b/mod/smilies.php
      @@ -4,11 +4,11 @@
        * @file mod/smilies.php
        */
       
      -require_once("include/smilies.php");
      +require_once("include/Smilies.php");
       
       function smilies_content(&$a) {
       	if ($a->argv[1]==="json"){
      -		$tmp = smilies::list_smilies();
      +		$tmp = Smilies::get_list();
       		$results = array();
       		for($i = 0; $i < count($tmp['texts']); $i++) {
       			$results[] = array('text' => $tmp['texts'][$i], 'icon' => $tmp['icons'][$i]);
      
      From ce1faf17d13d3a2f9dcfc311871b7a152baa69bc Mon Sep 17 00:00:00 2001
      From: rabuzarus <>
      Date: Thu, 14 Apr 2016 17:05:19 +0200
      Subject: [PATCH 13/17] rework autocomplete: some polishing
      
      ---
       include/DirSearch.php     | 6 ++++--
       include/Smilies.php       | 6 ++++--
       include/acl_selectors.php | 4 ++--
       include/network.php       | 2 +-
       mod/smilies.php           | 2 +-
       5 files changed, 12 insertions(+), 8 deletions(-)
      
      diff --git a/include/DirSearch.php b/include/DirSearch.php
      index a6a0b59fc..8682cb18e 100644
      --- a/include/DirSearch.php
      +++ b/include/DirSearch.php
      @@ -1,7 +1,8 @@
       
      Date: Thu, 14 Apr 2016 23:59:29 +0200
      Subject: [PATCH 14/17] rework autocomplete: seperate bbcode completion
      
      ---
       js/autocomplete.js                           | 122 +++++++++++++++++--
       js/main.js                                   |   2 +
       view/templates/event_head.tpl                |   3 +
       view/templates/jot-header.tpl                |   1 +
       view/templates/msg-header.tpl                |   5 +
       view/theme/smoothly/templates/jot-header.tpl |   1 +
       view/theme/vier/templates/event_form.tpl     |   2 +-
       7 files changed, 124 insertions(+), 12 deletions(-)
      
      diff --git a/js/autocomplete.js b/js/autocomplete.js
      index f31161ff8..053cc9e3a 100644
      --- a/js/autocomplete.js
      +++ b/js/autocomplete.js
      @@ -111,15 +111,70 @@ function submit_form(e) {
       	$(e).parents('form').submit();
       }
       
      +function getWord(text, caretPos) {
      +	var index = text.indexOf(caretPos);
      +	var postText = text.substring(caretPos, caretPos+8);
      +	if ((postText.indexOf("[/list]") > 0) || postText.indexOf("[/ul]") > 0 || postText.indexOf("[/ol]") > 0) {
      +		return postText;
      +	}
      +}
      +
      +function getCaretPosition(ctrl) {
      +	var CaretPos = 0;   // IE Support
      +	if (document.selection) {
      +		ctrl.focus();
      +		var Sel = document.selection.createRange();
      +		Sel.moveStart('character', -ctrl.value.length);
      +		CaretPos = Sel.text.length;
      +	}
      +	// Firefox support
      +	else if (ctrl.selectionStart || ctrl.selectionStart == '0')
      +		CaretPos = ctrl.selectionStart;
      +	return (CaretPos);
      +}
      +
      +function setCaretPosition(ctrl, pos){
      +	if(ctrl.setSelectionRange) {
      +		ctrl.focus();
      +		ctrl.setSelectionRange(pos,pos);
      +	}
      +	else if (ctrl.createTextRange) {
      +		var range = ctrl.createTextRange();
      +		range.collapse(true);
      +		range.moveEnd('character', pos);
      +		range.moveStart('character', pos);
      +		range.select();
      +	}
      +}
      +
      +function listNewLineAutocomplete(id) {
      +	var text = document.getElementById(id);
      +	var caretPos = getCaretPosition(text)
      +	var word = getWord(text.value, caretPos);
      +	if (word != null) {
      +		var textBefore = text.value.substring(0, caretPos);
      +		var textAfter  = text.value.substring(caretPos, text.length);
      +		$('#' + id).val(textBefore + '\r\n[*] ' + textAfter);
      +		setCaretPosition(text, caretPos + 5);
      +		return true;
      +	}
      +}
      +
      +function string2bb(element) {
      +	if(element == 'bold') return 'b';
      +	else if(element == 'italic') return 'i';
      +	else if(element == 'underline') return 'u';
      +	else if(element == 'overline') return 'o';
      +	else if(element == 'strike') return 's';
      +	else return element;
      +}
      +
       /**
        * jQuery plugin 'editor_autocomplete'
        */
       (function( $ ) {
       	$.fn.editor_autocomplete = function(backend_url) {
       
      -		// 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' ];
      -
       		// Autocomplete contacts
       		contacts = {
       			match: /(^|\s)(@\!*)([^ \n]+)$/,
      @@ -138,15 +193,8 @@ function submit_form(e) {
       			replace: function(item) { return "$1" + item.text + ' '; },
       		};
       
      -		// Autocomplete BBTags
      -		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 + ']']; },
      -		};
       		this.attr('autocomplete','off');
      -		this.textcomplete([contacts,smilies, bbtags], {className:'acpopup', zIndex:1020});
      +		this.textcomplete([contacts,smilies], {className:'acpopup', zIndex:1020});
       	};
       })( jQuery );
       
      @@ -229,6 +277,58 @@ function submit_form(e) {
       	};
       })( jQuery );
       
      +(function( $ ) {
      +	$.fn.bbco_autocomplete = function(type) {
      +
      +		if(type=='bbcode') {
      +			var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'quote', 'code', 'spoiler', 'map', 'nobb', 'list', 'ul', 'ol', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size'];
      +			var open_elements = ['*', 'hr'];
      +
      +			var elements = open_close_elements.concat(open_elements);
      +		}
      +
      +		bbco = {
      +			match: /\[(\w*\**)$/,
      +			search: function (term, callback) {
      +				callback($.map(elements, function (element) {
      +					return element.indexOf(term) === 0 ? element : null;
      +				}));
      +			},
      +			index: 1,
      +			replace: function (element) {
      +				element = string2bb(element);
      +				if(open_elements.indexOf(element) < 0) {
      +					if(element === 'list' || element === 'ol' || element === 'ul') {
      +						return ['\[' + element + '\]' + '\n\[*\] ', '\n\[/' + element + '\]'];
      +					}
      +					else if(element === 'table') {
      +						return ['\[' + element + '\]' + '\n\[tr\]', '\[/tr\]\n\[/' + element + '\]'];
      +					}
      +					else {
      +						return ['\[' + element + '\]', '\[/' + element + '\]'];
      +					}
      +				}
      +				else {
      +					return '\[' + element + '\] ';
      +				}
      +			}
      +		};
      +
      +		this.attr('autocomplete','off');
      +		var a = this.textcomplete([bbco], {className:'acpopup', zIndex:1020});
      +
      +		a.on('textComplete:select', function(e, value, strategy) { value; });
      +
      +		a.keypress(function(e){
      +			e.stopImmediatePropagation();
      +			if (e.keyCode == 13) {
      +				var x = listNewLineAutocomplete(this.id);
      +				if(x)
      +					e.preventDefault();
      +			}
      +		});
      +	};
      +})( jQuery );
       
       /**
        * Friendica people autocomplete legacy
      diff --git a/js/main.js b/js/main.js
      index 1ed0c4b6a..7e1c22ecf 100644
      --- a/js/main.js
      +++ b/js/main.js
      @@ -495,6 +495,8 @@
       			}
       			/* autocomplete @nicknames */
       			$(".comment-edit-form  textarea").editor_autocomplete(baseurl+"/acl");
      +			/* autocomplete bbcode */
      + +			$(".comment-edit-form  textarea").bbco_autocomplete('bbcode');
       
       			// setup videos, since VideoJS won't take care of any loaded via AJAX
       			if(typeof videojs != 'undefined') videojs.autoSetup();
      diff --git a/view/templates/event_head.tpl b/view/templates/event_head.tpl
      index 745327e99..de5ad6070 100644
      --- a/view/templates/event_head.tpl
      +++ b/view/templates/event_head.tpl
      @@ -148,6 +148,9 @@
       
       
       	$(document).ready(function() { 
      +		{{if $editselect = 'none'}}
      +		$("#comment-edit-text-desc").bbco_autocomplete('bbcode');
      +		{{/if}}
       
       		$('#event-share-checkbox').change(function() {
       
      diff --git a/view/templates/jot-header.tpl b/view/templates/jot-header.tpl
      index d12293f04..7498bfc42 100644
      --- a/view/templates/jot-header.tpl
      +++ b/view/templates/jot-header.tpl
      @@ -24,6 +24,7 @@ function initEditor(cb){
       			$("#profile-jot-text-loading").hide();
       			$("#profile-jot-text").css({ 'height': 200, 'color': '#000' });
       			$("#profile-jot-text").editor_autocomplete(baseurl+"/acl");
      +			$("#profile-jot-text").bbco_autocomplete('bbcode');
       			editor = true;
       			$("a#jot-perms-icon").colorbox(colorbox_options);
       			$(".jothidden").show();
      diff --git a/view/templates/msg-header.tpl b/view/templates/msg-header.tpl
      index 9b1a92ef5..ca9192352 100644
      --- a/view/templates/msg-header.tpl
      +++ b/view/templates/msg-header.tpl
      @@ -63,6 +63,11 @@ else
       			}
       		);
       
      +		{{if $editselect = 'none'}}
      +		$("#prvmail-text").bbco_autocomplete('bbcode');
      +		{{/if}}
      +
      +
       	});
       
       	function jotGetLink() {
      diff --git a/view/theme/smoothly/templates/jot-header.tpl b/view/theme/smoothly/templates/jot-header.tpl
      index 8b2666f0f..880764da0 100644
      --- a/view/theme/smoothly/templates/jot-header.tpl
      +++ b/view/theme/smoothly/templates/jot-header.tpl
      @@ -13,6 +13,7 @@ function initEditor(cb){
       			$("#profile-jot-text-loading").hide();
                   		$("#profile-jot-text").css({ 'height': 200, 'color': '#000' });
       			$("#profile-jot-text").editor_autocomplete(baseurl+"/acl");
      +			$("#profile-jot-text").bbco_autocomplete('bbcode');
                   		$(".jothidden").show();
                   		editor = true;
                   		$("a#jot-perms-icon").colorbox({
      diff --git a/view/theme/vier/templates/event_form.tpl b/view/theme/vier/templates/event_form.tpl
      index 165234c26..4f3240de8 100644
      --- a/view/theme/vier/templates/event_form.tpl
      +++ b/view/theme/vier/templates/event_form.tpl
      @@ -34,7 +34,7 @@
       
       
       
      {{$d_text}}
      - +
      From bbab3f590a36a7d23267451abf93ac5b21a39d0a Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Fri, 15 Apr 2016 02:02:54 +0200 Subject: [PATCH 15/17] rework autocomplete: update jquery.textcomplete to v1.3.3 --- js/autocomplete.js | 10 +- library/jquery-textcomplete/CHANGELOG.md | 335 ++++++++++++++++++ library/jquery-textcomplete/README.md | 54 +++ .../jquery.textcomplete.css | 33 ++ .../jquery.textcomplete.js | 308 ++++++++++++---- .../jquery.textcomplete.min.js | 3 + .../jquery.textcomplete.min.map | 1 + 7 files changed, 671 insertions(+), 73 deletions(-) create mode 100644 library/jquery-textcomplete/CHANGELOG.md create mode 100644 library/jquery-textcomplete/README.md create mode 100644 library/jquery-textcomplete/jquery.textcomplete.css create mode 100644 library/jquery-textcomplete/jquery.textcomplete.min.js create mode 100644 library/jquery-textcomplete/jquery.textcomplete.min.map diff --git a/js/autocomplete.js b/js/autocomplete.js index 053cc9e3a..dfe0bb320 100644 --- a/js/autocomplete.js +++ b/js/autocomplete.js @@ -194,7 +194,7 @@ function string2bb(element) { }; this.attr('autocomplete','off'); - this.textcomplete([contacts,smilies], {className:'acpopup', zIndex:1020}); + this.textcomplete([contacts,smilies], {className:'acpopup', zIndex:10000}); }; })( jQuery ); @@ -221,7 +221,7 @@ function string2bb(element) { template: contact_format, }; this.attr('autocomplete', 'off'); - var a = this.textcomplete([contacts, community], {className:'acpopup', maxCount:100, zIndex: 1020, appendTo:'#nav-search-box'}); + var a = this.textcomplete([contacts, community], {className:'acpopup', maxCount:100, zIndex: 10000, appendTo:'nav'}); a.on('textComplete:select', function(e, value, strategy) { submit_form(this); }); }; })( jQuery ); @@ -241,7 +241,7 @@ function string2bb(element) { }; this.attr('autocomplete','off'); - var a = this.textcomplete([contacts], {className:'acpopup', zIndex:1020}); + var a = this.textcomplete([contacts], {className:'acpopup', zIndex:10000}); if(autosubmit) a.on('textComplete:select', function(e,value,strategy) { submit_form(this); }); @@ -267,7 +267,7 @@ function string2bb(element) { }; this.attr('autocomplete','off'); - var a = this.textcomplete([names], {className:'acpopup', zIndex:1020}); + var a = this.textcomplete([names], {className:'acpopup', zIndex:10000}); if(autosubmit) a.on('textComplete:select', function(e,value,strategy) { submit_form(this); }); @@ -315,7 +315,7 @@ function string2bb(element) { }; this.attr('autocomplete','off'); - var a = this.textcomplete([bbco], {className:'acpopup', zIndex:1020}); + var a = this.textcomplete([bbco], {className:'acpopup', zIndex:10000}); a.on('textComplete:select', function(e, value, strategy) { value; }); diff --git a/library/jquery-textcomplete/CHANGELOG.md b/library/jquery-textcomplete/CHANGELOG.md new file mode 100644 index 000000000..8f737c3e3 --- /dev/null +++ b/library/jquery-textcomplete/CHANGELOG.md @@ -0,0 +1,335 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +This project adheres to [Semantic Versioning](http://semver.org/) by version 1.0.0. + +This change log adheres to [keepachangelog.com](http://keepachangelog.com). + +## [Unreleased] + +## [1.3.3] - 2016-04-04 +### Fixed +- Fix uncaught TypeError. + +## [1.3.2] - 2016-03-27 +### Fixed +- Fix dropdown position problem with `line-height: normal`. + +## [1.3.1] - 2016-03-23 +### Fixed +- Fix `input[type=search]` support. + +## [1.3.0] - 2016-03-20 +### Added +- Add optional "id" strategy parameter. + +## [1.2.2] - 2016-03-19 +### Fixed +- Remove dropdown element after `textcomplete('destroy')`. +- Skip search after pressing tab. +- Fix dropdown-menu positioning problem using textarea-caret package. + +## [1.2.1] - 2016-03-14 +### Fixed +- Build dist files. + +## [1.2.0] - 2016-03-14 +### Added +- Support `input[type=search]` ([#236](https://github.com/yuku-t/jquery-textcomplete/pull/236)) + +## [1.1.0] - 2016-03-10 +### Added +- Add the ability to insert HTML into a "contenteditable" field. ([#217](https://github.com/yuku-t/jquery-textcomplete/pull/217)) + +### Fixed +- Position relative to appendTo element. ([#234](https://github.com/yuku-t/jquery-textcomplete/pull/234)) +- Avoid dropdown bumping into right edge of window. ([#235](https://github.com/yuku-t/jquery-textcomplete/pull/235)) +- Fix top position issue when window is scrolled up and parents has fix position. ([#229](https://github.com/yuku-t/jquery-textcomplete/pull/229)) + +## [1.0.0] - 2016-02-29 +### Changed +- Adheres keepachangelog.com. + +## [0.8.2] - 2016-02-29 +### Added +- Add deactivate method to Completer. ([#233](https://github.com/yuku-t/jquery-textcomplete/pull/233)) + +## [0.8.1] - 2015-10-22 +### Added +- Add condition to ignore skipUnchangedTerm for empty text. ([#210](https://github.com/yuku-t/jquery-textcomplete/pull/210)) + +## [0.8.0] - 2015-08-31 +### Changed +- If undefined is returned from a replace callback dont replace the text. ([#204](https://github.com/yuku-t/jquery-textcomplete/pull/204)) + +## [0.7.3] - 2015-08-27 +### Added +- Add `Strategy#el` and `Strategy#$el` which returns current input/textarea element and corresponding jquery object respectively. + +## [0.7.2] - 2015-08-26 +### Fixed +- Reset \_term after selected ([#170](https://github.com/yuku-t/jquery-textcomplete/pull/170)) + +## [0.7.1] - 2015-08-19 +### Changed +- Remove RTL support because of some bugs. + +## [0.7.0] - 2015-07-02 +### Add +- Add support for a "no results" message like the header/footer. ([#179](https://github.com/yuku-t/jquery-textcomplete/pull/179)) +- Yield the search term to the template function. ([#177](https://github.com/yuku-t/jquery-textcomplete/pull/177)) +- Add amd wrapper. ([#167](https://github.com/yuku-t/jquery-textcomplete/pull/167)) +- Add touch devices support. ([#163](https://github.com/yuku-t/jquery-textcomplete/pull/163)) + +### Changed +- Stop sharing a dropdown element. + +## [0.6.1] - 2015-06-30 +### Fixed +- Fix bug that Dropdown.\_fitToBottom does not consider window scroll + +## [0.6.0] - 2015-06-30 +### Added +- Now dropdown elements have "textcomplete-dropdown" class. + +## [0.5.2] - 2015-06-29 +### Fixed +- Keep dropdown list in browser window. ([#172](https://github.com/yuku-t/jquery-textcomplete/pull/172)) + +## [0.5.1] - 2015-06-08 +### Changed +- Now a replace function is invoked with a user event. + +## [0.5.0] - 2015-06-08 +### Added +- Support `onKeydown` option. + +## [0.4.0] - 2015-03-10 +### Added +- Publish to [npmjs](https://www.npmjs.com/package/jquery-textcomplete). +- Support giving a function which returns a regexp to `match` option for dynamic matching. + +## [0.3.9] - 2015-03-03 +### Fixed +- Deactivate dropdown on escape. ([#155](https://github.com/yuku-t/jquery-textcomplete/pull/155)) + +## [0.3.8] - 2015-02-26 +### Fixed +- Fix completion with enter key. ([#154](https://github.com/yuku-t/jquery-textcomplete/pull/154)) +- Fix empty span node is inserted. ([#153](https://github.com/yuku-t/jquery-textcomplete/pull/153)) + +## [0.3.7] - 2015-01-21 +### Added +- Support input([type=text]. [#149](https://github.com/yuku-t/jquery-textcomplete/pull/149)) + +## [0.3.6] - 2014-12-11 +### Added +- Support element.contentEditable compatibility check. ([#147](https://github.com/yuku-t/jquery-textcomplete/pull/147)) + +### Fixed +- Fixes the fire function for events with additional parameters. ([#145](https://github.com/yuku-t/jquery-textcomplete/pull/145)) + +## [0.3.5] - 2014-12-11 +### Added +- Adds functionality to complete selection on space key. ([#141](https://github.com/yuku-t/jquery-textcomplete/pull/141)) + +### Fixed +- Loading script in head and destroy method bugfixes. ([#143](https://github.com/yuku-t/jquery-textcomplete/pull/143)) + +## [0.3.4] - 2014-12-03 +### Fixed +- Fix error when destroy is called before the field is focused. ([#138](https://github.com/yuku-t/jquery-textcomplete/pull/138)) +- Fix IE bug where it would only trigger when tha carrot was at the end of the line. ([#133](https://github.com/yuku-t/jquery-textcomplete/pull/133)) + +## [0.3.3] - 2014-09-25 +### Added +- Add `className` option. +- Add `match` as the third argument of a search function. + +### Fixed +- Ignore `.textcomplete('destory')` on non-initialized elements. ([#118](https://github.com/yuku-t/jquery-textcomplete/pull/118)) +- Trigger completer with the current text by default. ([#119](https://github.com/yuku-t/jquery-textcomplete/pull/119)) +- Hide dropdown before destroying it. ([#120](https://github.com/yuku-t/jquery-textcomplete/pull/120)) +- Don't throw an exception even if a jquery click event is manually triggered. ([#121](https://github.com/yuku-t/jquery-textcomplete/pull/121)) + +## [0.3.2] - 2014-09-16 +### Added +- Add `IETextarea` adapter which supports IE8 +- Add `idProperty` option. +- Add `adapter` option. + +### Changed +- Rename `Input` as `Adapter`. + +## [0.3.1] - 2014-09-10 +### Added +- Add `context` strategy option. +- Add `debounce` option. + +### Changed +- Recycle `.dropdown-menu` element if available. + +## [0.3.0] - 2014-09-10 +### Added +- Consider the `tab-size` of textarea. +- Add `zIndex` option. + +### Fixed +- Revive `header` and `footer` options. +- Revive `height` option. + +## [0.3.0-beta2] - 2014-09-09 +### Fixed +- Make sure that all demos work fine. + +## [0.3.0-beta1] - 2014-08-31 +### Fixed +- Huge refactoring. + +## [0.2.6] - 2014-08-16 +### Fixed +- Repair contenteditable. + +## [0.2.5] - 2014-08-07 +### Added +- Enhance contenteditable support. ([#98](https://github.com/yuku-t/jquery-textcomplete/pull/98)) +- Support absolute left/right placement. ([#96](https://github.com/yuku-t/jquery-textcomplete/pull/96)) +- Support absolute height, scrollbar, pageup and pagedown. ([#87](https://github.com/yuku-t/jquery-textcomplete/pull/87)) + +## [0.2.4] - 2014-07-02 +### Fixed +- Fix horizonal position on contentEditable elements. ([#92](https://github.com/yuku-t/jquery-textcomplete/pull/92)) + +## [0.2.3] - 2014-06-24 +### Added +- Option to supply list view position function. ([#88](https://github.com/yuku-t/jquery-textcomplete/pull/88)) + +## [0.2.2] - 2014-06-08 +### Added +- Append dropdown element to body element by default. +- Tiny refactoring. [#84] +- Ignore tab key when modifier keys are being pushed. ([#85](https://github.com/yuku-t/jquery-textcomplete/pull/85)) +- Manual triggering. + +## [0.2.1] - 2014-05-15 +### Added +- Support `appendTo` option. +- `header` and `footer` supports a function. + +### Changed +- Remove textcomplate-wrapper element. + +## [0.2.0] - 2014-05-02 +### Added +- Contenteditable support. +- Several bugfixes. +- Support `header` and `footer` setting. + +## [0.1.4.1] - 2014-04-04 +### Added +- Support placement option. +- Emacs-style prev/next keybindings. +- Replay searchFunc for the last term on slow network env. + +### Fixed +- Several bugfixes. + +## [0.1.3] - 2014-04-07 +### Added +- Support RTL positioning. + +### Fixed +- Several bugfixes. + +## [0.1.2] - 2014-02-08 +### Added +- Enable to append strategies on the fly. +- Enable to stop autocompleting. +- Enable to apply multiple textareas at once. +- Don't show popup on pressing arrow up and down keys. +- Hide dropdown by pressing ESC key. +- Prevent showing a dropdown when it just autocompleted. + +## [0.1.1] - 2014-02-02 +### Added +- Introduce `textComplete:show`, `textComplete:hide` and `textComplete:select` events. + +## [0.1.0] - 2013-10-28 +### Added +- Now strategies argument is an Array of strategy objects. + +## [0.0.4] - 2013-10-28 +### Added +- Up and Down arrows cycle instead of exit. +- Support Zepto. +- Support jQuery.overlay. + +### Fixed +- Several bugfixes. + +## [0.0.3] - 2013-09-11 +### Added +- Some performance improvement. +- Implement lazy callbacking on search function. + +## [0.0.2] - 2013-09-08 +### Added +- Support IE8. +- Some performance improvement. +- Implement cache option. + +## 0.0.1 - 2013-09-02 +### Added +- Initial release. + +[Unreleased]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.3...HEAD +[1.3.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.2...v1.3.3 +[1.3.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.1...v1.3.2 +[1.3.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.0...v1.3.1 +[1.3.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.2...v1.3.0 +[1.2.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.1...v1.2.2 +[1.2.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.0...v1.2.1 +[1.2.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.2...v1.0.0 +[0.8.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.1...v0.8.2 +[0.8.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.0...v0.8.1 +[0.8.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.3...v0.8.0 +[0.7.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.2...v0.7.3 +[0.7.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.1...v0.7.2 +[0.7.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.0...v0.7.1 +[0.7.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.6.1...v0.7.0 +[0.6.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.6.0...v0.6.1 +[0.6.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.2...v0.6.0 +[0.5.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.1...v0.5.2 +[0.5.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.0...v0.5.1 +[0.5.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.4.0...v0.5.0 +[0.4.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.9...v0.4.0 +[0.3.9]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.8...v0.3.9 +[0.3.8]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.7...v0.3.8 +[0.3.7]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.6...v0.3.7 +[0.3.6]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.5...v0.3.6 +[0.3.5]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.4...v0.3.5 +[0.3.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.3...v0.3.4 +[0.3.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.2...v0.3.3 +[0.3.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0-beta2...v0.3.0 +[0.3.0-beta2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0-beta1...v0.3.0-beta2 +[0.3.0-beta1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.6...v0.3.0-beta1 +[0.2.6]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.5...v0.2.6 +[0.2.5]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.4...v0.2.5 +[0.2.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.3...v0.2.4 +[0.2.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.2...v0.2.3 +[0.2.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.1...v0.2.2 +[0.2.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.4.1...v0.2.0 +[0.1.4.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.3...v0.1.4.1 +[0.1.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.2...v0.1.3 +[0.1.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.4...v0.1.0 +[0.0.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.3...v0.0.4 +[0.0.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.2...v0.0.3 +[0.0.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.1...v0.0.2 diff --git a/library/jquery-textcomplete/README.md b/library/jquery-textcomplete/README.md new file mode 100644 index 000000000..890155e12 --- /dev/null +++ b/library/jquery-textcomplete/README.md @@ -0,0 +1,54 @@ +Autocomplete for Textarea +========================= + +[![npm version](https://badge.fury.io/js/jquery-textcomplete.svg)](http://badge.fury.io/js/jquery-textcomplete) +[![Bower version](https://badge.fury.io/bo/jquery-textcomplete.svg)](http://badge.fury.io/bo/jquery-textcomplete) +[![Analytics](https://ga-beacon.appspot.com/UA-4932407-14/jquery-textcomplete/readme)](https://github.com/igrigorik/ga-beacon) + +Introduces autocompleting power to textareas, like a GitHub comment form has. + +![Demo](http://yuku-t.com/jquery-textcomplete/media/images/demo.gif) + +[Demo](http://yuku-t.com/jquery-textcomplete/). + +Synopsis +-------- + +```js +$('textarea').textcomplete([{ + match: /(^|\b)(\w{2,})$/, + search: function (term, callback) { + var words = ['google', 'facebook', 'github', 'microsoft', 'yahoo']; + callback($.map(words, function (word) { + return word.indexOf(term) === 0 ? word : null; + })); + }, + replace: function (word) { + return word + ' '; + } +}]); +``` + +Dependencies +------------ + +- jQuery (>= 1.7.0) OR Zepto (>= 1.0) + +Documents +--------- + +See [doc](https://github.com/yuku-t/jquery-textcomplete/tree/master/doc) dir. + +License +------- + +Licensed under the MIT License. + +Credits +------- + +### Contributors + +Patches and code improvements were contributed by: + +https://github.com/yuku-t/jquery-textcomplete/graphs/contributors diff --git a/library/jquery-textcomplete/jquery.textcomplete.css b/library/jquery-textcomplete/jquery.textcomplete.css new file mode 100644 index 000000000..37a761b7e --- /dev/null +++ b/library/jquery-textcomplete/jquery.textcomplete.css @@ -0,0 +1,33 @@ +/* Sample */ + +.dropdown-menu { + border: 1px solid #ddd; + background-color: white; +} + +.dropdown-menu li { + border-top: 1px solid #ddd; + padding: 2px 5px; +} + +.dropdown-menu li:first-child { + border-top: none; +} + +.dropdown-menu li:hover, +.dropdown-menu .active { + background-color: rgb(110, 183, 219); +} + + +/* SHOULD not modify */ + +.dropdown-menu { + list-style: none; + padding: 0; + margin: 0; +} + +.dropdown-menu a:hover { + cursor: pointer; +} diff --git a/library/jquery-textcomplete/jquery.textcomplete.js b/library/jquery-textcomplete/jquery.textcomplete.js index 200310175..1622b77c6 100644 --- a/library/jquery-textcomplete/jquery.textcomplete.js +++ b/library/jquery-textcomplete/jquery.textcomplete.js @@ -149,7 +149,7 @@ if (typeof jQuery === 'undefined') { this.views = []; this.option = $.extend({}, Completer._getDefaults(), option); - if (!this.$el.is('input[type=text]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') { + if (!this.$el.is('input[type=text]') && !this.$el.is('input[type=search]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') { throw new Error('textcomplete must be called on a Textarea or a ContentEditable.'); } @@ -196,7 +196,7 @@ if (typeof jQuery === 'undefined') { if (this.option.adapter) { Adapter = this.option.adapter; } else { - if (this.$el.is('textarea') || this.$el.is('input[type=text]')) { + if (this.$el.is('textarea') || this.$el.is('input[type=text]') || this.$el.is('input[type=search]')) { viewName = typeof element.selectionEnd === 'number' ? 'Textarea' : 'IETextarea'; } else { viewName = 'ContentEditable'; @@ -217,6 +217,12 @@ if (typeof jQuery === 'undefined') { this.$el = this.adapter = this.dropdown = null; }, + deactivate: function () { + if (this.dropdown) { + this.dropdown.deactivate(); + } + }, + // Invoke textcomplete. trigger: function (text, skipUnchangedTerm) { if (!this.dropdown) { this.initialize(); } @@ -225,7 +231,7 @@ if (typeof jQuery === 'undefined') { if (searchQuery.length) { var term = searchQuery[1]; // Ignore shift-key, ctrl-key and so on. - if (skipUnchangedTerm && this._term === term) { return; } + if (skipUnchangedTerm && this._term === term && term !== "") { return; } this._term = term; this._search.apply(this, searchQuery); } else { @@ -432,6 +438,7 @@ if (typeof jQuery === 'undefined') { this.$el.off('.' + this.id); this.$inputEl.off('.' + this.id); this.clear(); + this.$el.remove(); this.$el = this.$inputEl = this.completer = null; delete dropdownViews[this.id] }, @@ -440,11 +447,18 @@ if (typeof jQuery === 'undefined') { var contentsHtml = this._buildContents(zippedData); var unzippedData = $.map(this.data, function (d) { return d.value; }); if (this.data.length) { + var strategy = zippedData[0].strategy; + if (strategy.id) { + this.$el.attr('data-strategy', strategy.id); + } else { + this.$el.removeAttr('data-strategy'); + } this._renderHeader(unzippedData); this._renderFooter(unzippedData); if (contentsHtml) { this._renderContents(contentsHtml); this._fitToBottom(); + this._fitToRight(); this._activateIndexedItem(); } this._setScroll(); @@ -456,8 +470,6 @@ if (typeof jQuery === 'undefined') { }, setPosition: function (pos) { - this.$el.css(this._applyPlacement(pos)); - // Make the dropdown fixed if the input is also fixed // This can't be done during init, as textcomplete may be used on multiple elements on the same page // Because the same dropdown is reused behind the scenes, we need to recheck every time the dropdown is showed @@ -467,10 +479,13 @@ if (typeof jQuery === 'undefined') { if($(this).css('position') === 'absolute') // The element has absolute positioning, so it's all OK return false; if($(this).css('position') === 'fixed') { + pos.top -= $window.scrollTop(); + pos.left -= $window.scrollLeft(); position = 'fixed'; return false; } }); + this.$el.css(this._applyPlacement(pos)); this.$el.css({ position: position }); // Update positioning return this; @@ -774,6 +789,17 @@ if (typeof jQuery === 'undefined') { } }, + _fitToRight: function() { + // We don't know how wide our content is until the browser positions us, and at that point it clips us + // to the document width so we don't know if we would have overrun it. As a heuristic to avoid that clipping + // (which makes our elements wrap onto the next line and corrupt the next item), if we're close to the right + // edge, move left. We don't know how far to move left, so just keep nudging a bit. + var tolerance = 30; // pixels. Make wider than vertical scrollbar because we might not be able to use that space. + while (this.$el.offset().left + this.$el.width() > $window.width() - tolerance) { + this.$el.offset({left: this.$el.offset().left - tolerance}); + } + }, + _applyPlacement: function (position) { // If the 'placement' option set to 'top', move the position above the element. if (this.placement.indexOf('top') !== -1) { @@ -843,6 +869,7 @@ if (typeof jQuery === 'undefined') { search: null, // Optional + id: null, cache: false, context: function () { return true; }, index: 2, @@ -932,11 +959,19 @@ if (typeof jQuery === 'undefined') { }, // Returns the caret's relative coordinates from body's left top corner. - // - // FIXME: Calculate the left top corner of `this.option.appendTo` element. getCaretPosition: function () { var position = this._getCaretRelativePosition(); var offset = this.$el.offset(); + + // Calculate the left top corner of `this.option.appendTo` element. + var $parent = this.option.appendTo; + if ($parent) { + if (!($parent instanceof $)) { $parent = $($parent); } + var parentOffset = $parent.offsetParent().offset(); + offset.top -= parentOffset.top; + offset.left -= parentOffset.left; + } + position.top += offset.top; position.left += offset.left; return position; @@ -962,6 +997,7 @@ if (typeof jQuery === 'undefined') { // Suppress searching if it returns true. _skipSearch: function (clickEvent) { switch (clickEvent.keyCode) { + case 9: // TAB case 13: // ENTER case 40: // DOWN case 38: // UP @@ -989,21 +1025,6 @@ if (typeof jQuery === 'undefined') { this.initialize(element, completer, option); } - Textarea.DIV_PROPERTIES = { - left: -9999, - position: 'absolute', - top: 0, - whiteSpace: 'pre-wrap' - } - - Textarea.COPY_PROPERTIES = [ - 'border-width', 'font-family', 'font-size', 'font-style', 'font-variant', - 'font-weight', 'height', 'letter-spacing', 'word-spacing', 'line-height', - 'text-decoration', 'text-align', 'width', 'padding-top', 'padding-right', - 'padding-bottom', 'padding-left', 'margin-top', 'margin-right', - 'margin-bottom', 'margin-left', 'border-style', 'box-sizing', 'tab-size' - ]; - $.extend(Textarea.prototype, $.fn.textcomplete.Adapter.prototype, { // Public methods // -------------- @@ -1024,56 +1045,38 @@ if (typeof jQuery === 'undefined') { } }, + getTextFromHeadToCaret: function () { + return this.el.value.substring(0, this.el.selectionEnd); + }, + // Private methods // --------------- - // Returns the caret's relative coordinates from textarea's left top corner. - // - // Browser native API does not provide the way to know the position of - // caret in pixels, so that here we use a kind of hack to accomplish - // the aim. First of all it puts a dummy div element and completely copies - // the textarea's style to the element, then it inserts the text and a - // span element into the textarea. - // Consequently, the span element's position is the thing what we want. _getCaretRelativePosition: function () { - var dummyDiv = $('
      ').css(this._copyCss()) - .text(this.getTextFromHeadToCaret()); - var span = $('').text('.').appendTo(dummyDiv); - this.$el.before(dummyDiv); - var position = span.position(); - position.top += span.height() - this.$el.scrollTop(); - position.lineHeight = span.height(); - dummyDiv.remove(); - return position; + var p = $.fn.textcomplete.getCaretCoordinates(this.el, this.el.selectionStart); + return { + top: p.top + this._calculateLineHeight() - this.$el.scrollTop(), + left: p.left - this.$el.scrollLeft() + }; }, - _copyCss: function () { - return $.extend({ - // Set 'scroll' if a scrollbar is being shown; otherwise 'auto'. - overflow: this.el.scrollHeight > this.el.offsetHeight ? 'scroll' : 'auto' - }, Textarea.DIV_PROPERTIES, this._getStyles()); - }, - - _getStyles: (function ($) { - var color = $('
      ').css(['color']).color; - if (typeof color !== 'undefined') { - return function () { - return this.$el.css(Textarea.COPY_PROPERTIES); - }; - } else { // jQuery < 1.8 - return function () { - var $el = this.$el; - var styles = {}; - $.each(Textarea.COPY_PROPERTIES, function (i, property) { - styles[property] = $el.css(property); - }); - return styles; - }; + _calculateLineHeight: function () { + var lineHeight = parseInt(this.$el.css('line-height'), 10); + if (isNaN(lineHeight)) { + // http://stackoverflow.com/a/4515470/1297336 + var parentNode = this.el.parentNode; + var temp = document.createElement(this.el.nodeName); + var style = this.el.style; + temp.setAttribute( + 'style', + 'margin:0px;padding:0px;font-family:' + style.fontFamily + ';font-size:' + style.fontSize + ); + temp.innerHTML = 'test'; + parentNode.appendChild(temp); + lineHeight = temp.clientHeight; + parentNode.removeChild(temp); } - })($), - - getTextFromHeadToCaret: function () { - return this.el.value.substring(0, this.el.selectionEnd); + return lineHeight; } }); @@ -1168,9 +1171,28 @@ if (typeof jQuery === 'undefined') { pre = pre.replace(strategy.match, newSubstr); range.selectNodeContents(range.startContainer); range.deleteContents(); - var node = document.createTextNode(pre + post); - range.insertNode(node); - range.setStart(node, pre.length); + + // create temporary elements + var preWrapper = document.createElement("div"); + preWrapper.innerHTML = pre; + var postWrapper = document.createElement("div"); + postWrapper.innerHTML = post; + + // create the fragment thats inserted + var fragment = document.createDocumentFragment(); + var childNode; + var lastOfPre; + while (childNode = preWrapper.firstChild) { + lastOfPre = fragment.appendChild(childNode); + } + while (childNode = postWrapper.firstChild) { + fragment.appendChild(childNode); + } + + // insert the fragment & jump behind the last node in "pre" + range.insertNode(fragment); + range.setStartAfter(lastOfPre); + range.collapse(true); sel.removeAllRanges(); sel.addRange(range); @@ -1223,5 +1245,155 @@ if (typeof jQuery === 'undefined') { $.fn.textcomplete.ContentEditable = ContentEditable; }(jQuery); +// The MIT License (MIT) +// +// Copyright (c) 2015 Jonathan Ong me@jongleberry.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. +// +// https://github.com/component/textarea-caret-position + +(function () { + +// The properties that we copy into a mirrored div. +// Note that some browsers, such as Firefox, +// do not concatenate properties, i.e. padding-top, bottom etc. -> padding, +// so we have to do every single property specifically. +var properties = [ + 'direction', // RTL support + 'boxSizing', + 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does + 'height', + 'overflowX', + 'overflowY', // copy the scrollbar for IE + + 'borderTopWidth', + 'borderRightWidth', + 'borderBottomWidth', + 'borderLeftWidth', + 'borderStyle', + + 'paddingTop', + 'paddingRight', + 'paddingBottom', + 'paddingLeft', + + // https://developer.mozilla.org/en-US/docs/Web/CSS/font + 'fontStyle', + 'fontVariant', + 'fontWeight', + 'fontStretch', + 'fontSize', + 'fontSizeAdjust', + 'lineHeight', + 'fontFamily', + + 'textAlign', + 'textTransform', + 'textIndent', + 'textDecoration', // might not make a difference, but better be safe + + 'letterSpacing', + 'wordSpacing', + + 'tabSize', + 'MozTabSize' + +]; + +var isBrowser = (typeof window !== 'undefined'); +var isFirefox = (isBrowser && window.mozInnerScreenX != null); + +function getCaretCoordinates(element, position, options) { + if(!isBrowser) { + throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser'); + } + + var debug = options && options.debug || false; + if (debug) { + var el = document.querySelector('#input-textarea-caret-position-mirror-div'); + if ( el ) { el.parentNode.removeChild(el); } + } + + // mirrored div + var div = document.createElement('div'); + div.id = 'input-textarea-caret-position-mirror-div'; + document.body.appendChild(div); + + var style = div.style; + var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9 + + // default textarea styles + style.whiteSpace = 'pre-wrap'; + if (element.nodeName !== 'INPUT') + style.wordWrap = 'break-word'; // only for textarea-s + + // position off-screen + style.position = 'absolute'; // required to return coordinates properly + if (!debug) + style.visibility = 'hidden'; // not 'display: none' because we want rendering + + // transfer the element's properties to the div + properties.forEach(function (prop) { + style[prop] = computed[prop]; + }); + + if (isFirefox) { + // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 + if (element.scrollHeight > parseInt(computed.height)) + style.overflowY = 'scroll'; + } else { + style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' + } + + div.textContent = element.value.substring(0, position); + // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 + if (element.nodeName === 'INPUT') + div.textContent = div.textContent.replace(/\s/g, '\u00a0'); + + var span = document.createElement('span'); + // Wrapping must be replicated *exactly*, including when a long word gets + // onto the next line, with whitespace at the end of the line before (#7). + // The *only* reliable way to do that is to copy the *entire* rest of the + // textarea's content into the created at the caret position. + // for inputs, just '.' would be enough, but why bother? + span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all + div.appendChild(span); + + var coordinates = { + top: span.offsetTop + parseInt(computed['borderTopWidth']), + left: span.offsetLeft + parseInt(computed['borderLeftWidth']) + }; + + if (debug) { + span.style.backgroundColor = '#aaa'; + } else { + document.body.removeChild(div); + } + + return coordinates; +} + +if (typeof module != 'undefined' && typeof module.exports != 'undefined') { + module.exports = getCaretCoordinates; +} else if(isBrowser){ + window.$.fn.textcomplete.getCaretCoordinates = getCaretCoordinates; +} + +}()); + return jQuery; })); diff --git a/library/jquery-textcomplete/jquery.textcomplete.min.js b/library/jquery-textcomplete/jquery.textcomplete.min.js new file mode 100644 index 000000000..fa3c2c032 --- /dev/null +++ b/library/jquery-textcomplete/jquery.textcomplete.min.js @@ -0,0 +1,3 @@ +/*! jquery-textcomplete - v1.3.2 - 2016-03-27 */ +!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"==typeof module&&module.exports){var b=require("jquery");module.exports=a(b)}else a(jQuery)}(function(a){if("undefined"==typeof a)throw new Error("jQuery.textcomplete requires jQuery");return+function(a){"use strict";var b=function(a){console.warn&&console.warn(a)},c=1;a.fn.textcomplete=function(d,e){var f=Array.prototype.slice.call(arguments);return this.each(function(){var g=this,h=a(this),i=h.data("textComplete");if(i||(e||(e={}),e._oid=c++,i=new a.fn.textcomplete.Completer(this,e),h.data("textComplete",i)),"string"==typeof d){if(!i)return;f.shift(),i[d].apply(i,f),"destroy"===d&&h.removeData("textComplete")}else a.each(d,function(c){a.each(["header","footer","placement","maxCount"],function(a){c[a]&&(i.option[a]=c[a],b(a+"as a strategy param is deprecated. Use option."),delete c[a])})}),i.register(a.fn.textcomplete.Strategy.parse(d,{el:g,$el:h}))})}}(a),+function(a){"use strict";function b(c,d){if(this.$el=a(c),this.id="textcomplete"+f++,this.strategies=[],this.views=[],this.option=a.extend({},b._getDefaults(),d),!(this.$el.is("input[type=text]")||this.$el.is("input[type=search]")||this.$el.is("textarea")||c.isContentEditable||"true"==c.contentEditable))throw new Error("textcomplete must be called on a Textarea or a ContentEditable.");if(c===document.activeElement)this.initialize();else{var e=this;this.$el.one("focus."+this.id,function(){e.initialize()})}}var c=function(a){var b,c;return function(){var d=Array.prototype.slice.call(arguments);if(b)return void(c=d);b=!0;var e=this;d.unshift(function f(){if(c){var d=c;c=void 0,d.unshift(f),a.apply(e,d)}else b=!1}),a.apply(this,d)}},d=function(a){return"[object String]"===Object.prototype.toString.call(a)},e=function(a){return"[object Function]"===Object.prototype.toString.call(a)},f=0;b._getDefaults=function(){return b.DEFAULTS||(b.DEFAULTS={appendTo:a("body"),zIndex:"100"}),b.DEFAULTS},a.extend(b.prototype,{id:null,option:null,strategies:null,adapter:null,dropdown:null,$el:null,initialize:function(){var b=this.$el.get(0);this.dropdown=new a.fn.textcomplete.Dropdown(b,this,this.option);var c,d;this.option.adapter?c=this.option.adapter:(d=this.$el.is("textarea")||this.$el.is("input[type=text]")||this.$el.is("input[type=search]")?"number"==typeof b.selectionEnd?"Textarea":"IETextarea":"ContentEditable",c=a.fn.textcomplete[d]),this.adapter=new c(b,this,this.option)},destroy:function(){this.$el.off("."+this.id),this.adapter&&this.adapter.destroy(),this.dropdown&&this.dropdown.destroy(),this.$el=this.adapter=this.dropdown=null},deactivate:function(){this.dropdown&&this.dropdown.deactivate()},trigger:function(a,b){this.dropdown||this.initialize(),null!=a||(a=this.adapter.getTextFromHeadToCaret());var c=this._extractSearchQuery(a);if(c.length){var d=c[1];if(b&&this._term===d&&""!==d)return;this._term=d,this._search.apply(this,c)}else this._term=null,this.dropdown.deactivate()},fire:function(a){var b=Array.prototype.slice.call(arguments,1);return this.$el.trigger(a,b),this},register:function(a){Array.prototype.push.apply(this.strategies,a)},select:function(a,b,c){this._term=null,this.adapter.select(a,b,c),this.fire("change").fire("textComplete:select",a,b),this.adapter.focus()},_clearAtNext:!0,_term:null,_extractSearchQuery:function(a){for(var b=0;b").addClass("dropdown-menu textcomplete-dropdown").attr("id","textcomplete-dropdown-"+b._oid).css({display:"none",left:0,position:"absolute",zIndex:b.zIndex}).appendTo(c);return d}}),a.extend(b.prototype,{$el:null,$inputEl:null,completer:null,footer:null,header:null,id:null,maxCount:10,placement:"",shown:!1,data:[],className:"",destroy:function(){this.deactivate(),this.$el.off("."+this.id),this.$inputEl.off("."+this.id),this.clear(),this.$el.remove(),this.$el=this.$inputEl=this.completer=null,delete e[this.id]},render:function(b){var c=this._buildContents(b),d=a.map(this.data,function(a){return a.value});if(this.data.length){var e=b[0].strategy;e.id?this.$el.attr("data-strategy",e.id):this.$el.removeAttr("data-strategy"),this._renderHeader(d),this._renderFooter(d),c&&(this._renderContents(c),this._fitToBottom(),this._fitToRight(),this._activateIndexedItem()),this._setScroll()}else this.noResultsMessage?this._renderNoResultsMessage(d):this.shown&&this.deactivate()},setPosition:function(b){var d="absolute";return this.$inputEl.add(this.$inputEl.parents()).each(function(){return"absolute"===a(this).css("position")?!1:"fixed"===a(this).css("position")?(b.top-=c.scrollTop(),b.left-=c.scrollLeft(),d="fixed",!1):void 0}),this.$el.css(this._applyPlacement(b)),this.$el.css({position:d}),this},clear:function(){this.$el.html(""),this.data=[],this._index=0,this._$header=this._$footer=this._$noResultsMessage=null},activate:function(){return this.shown||(this.clear(),this.$el.show(),this.className&&this.$el.addClass(this.className),this.completer.fire("textComplete:show"),this.shown=!0),this},deactivate:function(){return this.shown&&(this.$el.hide(),this.className&&this.$el.removeClass(this.className),this.completer.fire("textComplete:hide"),this.shown=!1),this},isUp:function(a){return 38===a.keyCode||a.ctrlKey&&80===a.keyCode},isDown:function(a){return 40===a.keyCode||a.ctrlKey&&78===a.keyCode},isEnter:function(a){var b=a.ctrlKey||a.altKey||a.metaKey||a.shiftKey;return!b&&(13===a.keyCode||9===a.keyCode||this.option.completeOnSpace===!0&&32===a.keyCode)},isPageup:function(a){return 33===a.keyCode},isPagedown:function(a){return 34===a.keyCode},isEscape:function(a){return 27===a.keyCode},_data:null,_index:null,_$header:null,_$noResultsMessage:null,_$footer:null,_bindEvents:function(){this.$el.on("mousedown."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("touchstart."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("mouseover."+this.id,".textcomplete-item",a.proxy(this._onMouseover,this)),this.$inputEl.on("keydown."+this.id,a.proxy(this._onKeydown,this))},_onClick:function(b){var c=a(b.target);b.preventDefault(),b.originalEvent.keepTextCompleteDropdown=this.id,c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item"));var d=this.data[parseInt(c.data("index"),10)];this.completer.select(d.value,d.strategy,b);var e=this;setTimeout(function(){e.deactivate(),"touchstart"===b.type&&e.$inputEl.focus()},0)},_onMouseover:function(b){var c=a(b.target);b.preventDefault(),c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item")),this._index=parseInt(c.data("index"),10),this._activateIndexedItem()},_onKeydown:function(b){if(this.shown){var c;switch(a.isFunction(this.option.onKeydown)&&(c=this.option.onKeydown(b,f)),null==c&&(c=this._defaultKeydown(b)),c){case f.KEY_UP:b.preventDefault(),this._up();break;case f.KEY_DOWN:b.preventDefault(),this._down();break;case f.KEY_ENTER:b.preventDefault(),this._enter(b);break;case f.KEY_PAGEUP:b.preventDefault(),this._pageup();break;case f.KEY_PAGEDOWN:b.preventDefault(),this._pagedown();break;case f.KEY_ESCAPE:b.preventDefault(),this.deactivate()}}},_defaultKeydown:function(a){return this.isUp(a)?f.KEY_UP:this.isDown(a)?f.KEY_DOWN:this.isEnter(a)?f.KEY_ENTER:this.isPageup(a)?f.KEY_PAGEUP:this.isPagedown(a)?f.KEY_PAGEDOWN:this.isEscape(a)?f.KEY_ESCAPE:void 0},_up:function(){0===this._index?this._index=this.data.length-1:this._index-=1,this._activateIndexedItem(),this._setScroll()},_down:function(){this._index===this.data.length-1?this._index=0:this._index+=1,this._activateIndexedItem(),this._setScroll()},_enter:function(a){var b=this.data[parseInt(this._getActiveElement().data("index"),10)];this.completer.select(b.value,b.strategy,a),this.deactivate()},_pageup:function(){var b=0,c=this._getActiveElement().position().top-this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top+a(this).outerHeight()>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_pagedown:function(){var b=this.data.length-1,c=this._getActiveElement().position().top+this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_activateIndexedItem:function(){this.$el.find(".textcomplete-item.active").removeClass("active"),this._getActiveElement().addClass("active")},_getActiveElement:function(){return this.$el.children(".textcomplete-item:nth("+this._index+")")},_setScroll:function(){var a=this._getActiveElement(),b=a.position().top,c=a.outerHeight(),d=this.$el.innerHeight(),e=this.$el.scrollTop();0===this._index||this._index==this.data.length-1||0>b?this.$el.scrollTop(b+e):b+c>d&&this.$el.scrollTop(b+c+e-d)},_buildContents:function(a){var b,c,e,f="";for(c=0;c',f+=b.strategy.template(b.value,b.term),f+="");return f},_renderHeader:function(b){if(this.header){this._$header||(this._$header=a('
    • ').prependTo(this.$el));var c=a.isFunction(this.header)?this.header(b):this.header;this._$header.html(c)}},_renderFooter:function(b){if(this.footer){this._$footer||(this._$footer=a('').appendTo(this.$el));var c=a.isFunction(this.footer)?this.footer(b):this.footer;this._$footer.html(c)}},_renderNoResultsMessage:function(b){if(this.noResultsMessage){this._$noResultsMessage||(this._$noResultsMessage=a('
    • ').appendTo(this.$el));var c=a.isFunction(this.noResultsMessage)?this.noResultsMessage(b):this.noResultsMessage;this._$noResultsMessage.html(c)}},_renderContents:function(a){this._$footer?this._$footer.before(a):this.$el.append(a)},_fitToBottom:function(){var a=c.scrollTop()+c.height(),b=this.$el.height();this.$el.position().top+b>a&&this.$el.offset({top:a-b})},_fitToRight:function(){for(var a=30;this.$el.offset().left+this.$el.width()>c.width()-a;)this.$el.offset({left:this.$el.offset().left-a})},_applyPlacement:function(a){return-1!==this.placement.indexOf("top")?a={top:"auto",bottom:this.$el.parent().height()-a.top+a.lineHeight,left:a.left}:(a.bottom="auto",delete a.lineHeight),-1!==this.placement.indexOf("absleft")?a.left=0:-1!==this.placement.indexOf("absright")&&(a.right=0,a.left="auto"),a}}),a.fn.textcomplete.Dropdown=b,a.extend(a.fn.textcomplete,f)}(a),+function(a){"use strict";function b(b){a.extend(this,b),this.cache&&(this.search=c(this.search))}var c=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}};b.parse=function(c,d){return a.map(c,function(a){var c=new b(a);return c.el=d.el,c.$el=d.$el,c})},a.extend(b.prototype,{match:null,replace:null,search:null,id:null,cache:!1,context:function(){return!0},index:2,template:function(a){return a},idProperty:null}),a.fn.textcomplete.Strategy=b}(a),+function(a){"use strict";function b(){}var c=Date.now||function(){return(new Date).getTime()},d=function(a,b){var d,e,f,g,h,i=function(){var j=c()-g;b>j?d=setTimeout(i,b-j):(d=null,h=a.apply(f,e),f=e=null)};return function(){return f=this,e=arguments,g=c(),d||(d=setTimeout(i,b)),h}};a.extend(b.prototype,{id:null,completer:null,el:null,$el:null,option:null,initialize:function(b,c,e){this.el=b,this.$el=a(b),this.id=c.id+this.constructor.name,this.completer=c,this.option=e,this.option.debounce&&(this._onKeyup=d(this._onKeyup,this.option.debounce)),this._bindEvents()},destroy:function(){this.$el.off("."+this.id),this.$el=this.el=this.completer=null},select:function(){throw new Error("Not implemented")},getCaretPosition:function(){var b=this._getCaretRelativePosition(),c=this.$el.offset(),d=this.option.appendTo;if(d){d instanceof a||(d=a(d));var e=d.offsetParent().offset();c.top-=e.top,c.left-=e.left}return b.top+=c.top,b.left+=c.left,b},focus:function(){this.$el.focus()},_bindEvents:function(){this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))},_onKeyup:function(a){this._skipSearch(a)||this.completer.trigger(this.getTextFromHeadToCaret(),!0)},_skipSearch:function(a){switch(a.keyCode){case 9:case 13:case 40:case 38:return!0}if(a.ctrlKey)switch(a.keyCode){case 78:case 80:return!0}}}),a.fn.textcomplete.Adapter=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(this.el.selectionEnd),g=c.replace(b,d);"undefined"!=typeof g&&(a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.selectionStart=this.el.selectionEnd=e.length)},getTextFromHeadToCaret:function(){return this.el.value.substring(0,this.el.selectionEnd)},_getCaretRelativePosition:function(){var b=a.fn.textcomplete.getCaretCoordinates(this.el,this.el.selectionStart);return{top:b.top+this._calculateLineHeight()-this.$el.scrollTop(),left:b.left-this.$el.scrollLeft()}},_calculateLineHeight:function(){var a=parseInt(this.$el.css("line-height"),10);if(isNaN(a)){var b=this.el.parentNode,c=document.createElement(this.el.nodeName),d=this.el.style;c.setAttribute("style","margin:0px;padding:0px;font-family:"+d.fontFamily+";font-size:"+d.fontSize),c.innerHTML="test",b.appendChild(c),a=c.clientHeight,b.removeChild(c)}return a}}),a.fn.textcomplete.Textarea=b}(a),+function(a){"use strict";function b(b,d,e){this.initialize(b,d,e),a(""+c+"").css({position:"absolute",top:-9999,left:-9999}).insertBefore(b)}var c="吶";a.extend(b.prototype,a.fn.textcomplete.Textarea.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(e.length),g=c.replace(b,d);if("undefined"!=typeof g){a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.focus();var h=this.el.createTextRange();h.collapse(!0),h.moveEnd("character",e.length),h.moveStart("character",e.length),h.select()}},getTextFromHeadToCaret:function(){this.el.focus();var a=document.selection.createRange();a.moveStart("character",-this.el.value.length);var b=a.text.split(c);return 1===b.length?b[0]:b[1]}}),a.fn.textcomplete.IETextarea=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=window.getSelection(),g=f.getRangeAt(0),h=g.cloneRange();h.selectNodeContents(g.startContainer);var i=h.toString(),j=i.substring(g.startOffset),k=c.replace(b,d);if("undefined"!=typeof k){a.isArray(k)&&(j=k[1]+j,k=k[0]),e=e.replace(c.match,k),g.selectNodeContents(g.startContainer),g.deleteContents();var l=document.createElement("div");l.innerHTML=e;var m=document.createElement("div");m.innerHTML=j;for(var n,o,p=document.createDocumentFragment();n=l.firstChild;)o=p.appendChild(n);for(;n=m.firstChild;)p.appendChild(n);g.insertNode(p),g.setStartAfter(o),g.collapse(!0),f.removeAllRanges(),f.addRange(g)}},_getCaretRelativePosition:function(){var b=window.getSelection().getRangeAt(0).cloneRange(),c=document.createElement("span");b.insertNode(c),b.selectNodeContents(c),b.deleteContents();var d=a(c),e=d.offset();return e.left-=this.$el.offset().left,e.top+=d.height()-this.$el.offset().top,e.lineHeight=d.height(),d.remove(),e},getTextFromHeadToCaret:function(){var a=window.getSelection().getRangeAt(0),b=a.cloneRange();return b.selectNodeContents(a.startContainer),b.toString().substring(0,a.startOffset)}}),a.fn.textcomplete.ContentEditable=b}(a),function(){function a(a,e,f){if(!c)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var g=f&&f.debug||!1;if(g){var h=document.querySelector("#input-textarea-caret-position-mirror-div");h&&h.parentNode.removeChild(h)}var i=document.createElement("div");i.id="input-textarea-caret-position-mirror-div",document.body.appendChild(i);var j=i.style,k=window.getComputedStyle?getComputedStyle(a):a.currentStyle;j.whiteSpace="pre-wrap","INPUT"!==a.nodeName&&(j.wordWrap="break-word"),j.position="absolute",g||(j.visibility="hidden"),b.forEach(function(a){j[a]=k[a]}),d?a.scrollHeight>parseInt(k.height)&&(j.overflowY="scroll"):j.overflow="hidden",i.textContent=a.value.substring(0,e),"INPUT"===a.nodeName&&(i.textContent=i.textContent.replace(/\s/g," "));var l=document.createElement("span");l.textContent=a.value.substring(e)||".",i.appendChild(l);var m={top:l.offsetTop+parseInt(k.borderTopWidth),left:l.offsetLeft+parseInt(k.borderLeftWidth)};return g?l.style.backgroundColor="#aaa":document.body.removeChild(i),m}var b=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],c="undefined"!=typeof window,d=c&&null!=window.mozInnerScreenX;"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a:c&&(window.$.fn.textcomplete.getCaretCoordinates=a)}(),a}); +//# sourceMappingURL=dist/jquery.textcomplete.min.map \ No newline at end of file diff --git a/library/jquery-textcomplete/jquery.textcomplete.min.map b/library/jquery-textcomplete/jquery.textcomplete.min.map new file mode 100644 index 000000000..55331a36a --- /dev/null +++ b/library/jquery-textcomplete/jquery.textcomplete.min.map @@ -0,0 +1 @@ +{"version":3,"file":"dist/jquery.textcomplete.min.js","sources":["dist/jquery.textcomplete.js"],"names":["factory","define","amd","module","exports","$","require","jQuery","Error","warn","message","console","id","fn","textcomplete","strategies","option","args","Array","prototype","slice","call","arguments","this","each","self","$this","completer","data","_oid","Completer","shift","apply","removeData","obj","name","register","Strategy","parse","el","$el","element","uniqueId","views","extend","_getDefaults","is","isContentEditable","contentEditable","document","activeElement","initialize","one","lock","func","locked","queuedArgsToReplay","unshift","replayOrFree","replayArgs","undefined","isString","Object","toString","isFunction","DEFAULTS","appendTo","zIndex","adapter","dropdown","get","Dropdown","Adapter","viewName","selectionEnd","destroy","off","deactivate","trigger","text","skipUnchangedTerm","getTextFromHeadToCaret","searchQuery","_extractSearchQuery","length","term","_term","_search","fire","eventName","push","select","value","strategy","e","focus","_clearAtNext","i","context","matchRegexp","match","index","free","search","stillSearching","shown","activate","clear","setPosition","getCaretPosition","render","_zip","map","createElement","_data","$inputEl","listPosition","height","_i","_bindEvents","dropdownViews","$window","window","include","zippedData","datum","elem","idProperty","on","originalEvent","keepTextCompleteDropdown","key","view","commands","SKIP_DEFAULT","KEY_UP","KEY_DOWN","KEY_ENTER","KEY_PAGEUP","KEY_PAGEDOWN","KEY_ESCAPE","$parent","addClass","attr","css","display","left","position","footer","header","maxCount","placement","className","remove","contentsHtml","_buildContents","unzippedData","d","removeAttr","_renderHeader","_renderFooter","_renderContents","_fitToBottom","_fitToRight","_activateIndexedItem","_setScroll","noResultsMessage","_renderNoResultsMessage","pos","add","parents","top","scrollTop","scrollLeft","_applyPlacement","html","_index","_$header","_$footer","_$noResultsMessage","show","hide","removeClass","isUp","keyCode","ctrlKey","isDown","isEnter","modifiers","altKey","metaKey","shiftKey","completeOnSpace","isPageup","isPagedown","isEscape","proxy","_onClick","_onMouseover","_onKeydown","target","preventDefault","hasClass","closest","parseInt","setTimeout","type","command","onKeydown","_defaultKeydown","_up","_down","_enter","_pageup","_pagedown","_getActiveElement","threshold","innerHeight","children","outerHeight","find","$activeEl","itemTop","itemHeight","visibleHeight","visibleTop","template","prependTo","before","append","windowScrollBottom","offset","tolerance","width","indexOf","bottom","parent","lineHeight","right","options","cache","memoize","memo","callback","concat","strategiesArray","params","strategyObj","replace","now","Date","getTime","debounce","wait","timeout","timestamp","result","later","last","constructor","_onKeyup","_getCaretRelativePosition","parentOffset","offsetParent","_skipSearch","clickEvent","Textarea","pre","post","substring","newSubstr","isArray","val","selectionStart","p","getCaretCoordinates","_calculateLineHeight","isNaN","parentNode","temp","nodeName","style","setAttribute","fontFamily","fontSize","innerHTML","appendChild","clientHeight","removeChild","IETextarea","sentinelChar","insertBefore","range","createTextRange","collapse","moveEnd","moveStart","selection","createRange","arr","split","ContentEditable","sel","getSelection","getRangeAt","cloneRange","selectNodeContents","startContainer","content","startOffset","deleteContents","preWrapper","postWrapper","childNode","lastOfPre","fragment","createDocumentFragment","firstChild","insertNode","setStartAfter","removeAllRanges","addRange","node","$node","isBrowser","debug","querySelector","div","body","computed","getComputedStyle","currentStyle","whiteSpace","wordWrap","visibility","properties","forEach","prop","isFirefox","scrollHeight","overflowY","overflow","textContent","span","coordinates","offsetTop","offsetLeft","backgroundColor","mozInnerScreenX"],"mappings":"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"} \ No newline at end of file From ed6ada0da571ac88b039a162601ac172c3680710 Mon Sep 17 00:00:00 2001 From: rabuzarus <> Date: Fri, 15 Apr 2016 02:25:58 +0200 Subject: [PATCH 16/17] rework autocomplete: use minified version + delete old fk.autocomplete.js --- js/fk.autocomplete.js | 203 ---------------------- util/minifyjs.sh | 1 - view/templates/head.tpl | 2 +- view/theme/frost-mobile/templates/end.tpl | 2 +- view/theme/frost/templates/end.tpl | 2 +- 5 files changed, 3 insertions(+), 207 deletions(-) delete mode 100644 js/fk.autocomplete.js diff --git a/js/fk.autocomplete.js b/js/fk.autocomplete.js deleted file mode 100644 index d7c81276b..000000000 --- a/js/fk.autocomplete.js +++ /dev/null @@ -1,203 +0,0 @@ -/** - * Friendica people autocomplete - * - * 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'; - /* style['max-height'] = '150px'; - style.border = '1px solid red'; - style.background = '#cccccc'; - - style.overflow = 'auto'; - style['z-index'] = '100000'; - */ - style.display = 'none'; - - this.cont = $("
      "); - 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 = " {1} ({2})".format(this.photo, this.name, this.addr); - 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 = $("
      "+label+"
      "); - 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(); - } -} - -function ContactAutocomplete(element,backend_url){ - this.pattern=/@([^ \n]+)$/; - this.popup=null; - var that = this; - - $(element).unbind('keydown'); - $(element).unbind('keyup'); - - $(element).keydown(function(event){ - if (that.popup!==null) that.popup.onkey(event); - }); - - $(element).keyup(function(event){ - cpos = $(this).getSelection(); - if (cpos.start==cpos.end){ - match = $(this).val().substring(0,cpos.start).match(that.pattern); - if (match!==null){ - if (that.popup===null){ - that.popup = new ACPopup(this, backend_url); - } - if (that.popup.ready && match[1]!==that.popup.searchText) that.popup.search(match[1]); - if (!that.popup.ready) that.popup=null; - - } else { - if (that.popup!==null) {that.popup.close(); that.popup=null;} - } - - - } - }); - -} - - -/** - * jQuery plugin 'contact_autocomplete' - */ -(function( $ ){ - $.fn.contact_autocomplete = function(backend_url) { - this.each(function(){ - new ContactAutocomplete(this, backend_url); - }); - }; -})( jQuery ); - - - - diff --git a/util/minifyjs.sh b/util/minifyjs.sh index 63d312f74..b7ed2d8c8 100755 --- a/util/minifyjs.sh +++ b/util/minifyjs.sh @@ -8,7 +8,6 @@ JSFILES=( "js/acl.js" "js/ajaxupload.js" "js/country.js" - "js/fk.autocomplete.js" "js/jquery.htmlstream.js" "js/main.js" "js/webtoolkit.base64.js" diff --git a/view/templates/head.tpl b/view/templates/head.tpl index 31555aae2..412323f32 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -33,7 +33,7 @@ - + diff --git a/view/theme/frost-mobile/templates/end.tpl b/view/theme/frost-mobile/templates/end.tpl index c1acbfb2a..04416ad00 100644 --- a/view/theme/frost-mobile/templates/end.tpl +++ b/view/theme/frost-mobile/templates/end.tpl @@ -7,7 +7,7 @@ - + diff --git a/view/theme/frost/templates/end.tpl b/view/theme/frost/templates/end.tpl index e864a9498..991ae7564 100644 --- a/view/theme/frost/templates/end.tpl +++ b/view/theme/frost/templates/end.tpl @@ -19,7 +19,7 @@ - + From b43f4722e6a9334cae458b074674e5081d2ae3e2 Mon Sep 17 00:00:00 2001 From: rabuzarus Date: Fri, 15 Apr 2016 03:17:15 +0200 Subject: [PATCH 17/17] rework autocomplete: fix .gitignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5b7e09b50..b300f579e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ addon *~ robots.txt -#ignore documentation, it should be newly built +#ignore documentation, it should be newly built doc/html #ignore reports, should be generted with every build @@ -23,7 +23,7 @@ report/ .buildpath .externalToolBuilders .settings -#ignore OSX .DS_Store files +#ignore OSX .DS_Store files .DS_Store /nbproject/private/