Интерфейс на основе candy
commit
6c69cdffcc
@ -0,0 +1,30 @@
|
||||
# Chat recall plugin
|
||||
This plugin will allow the user to navigate through historical messages they've typed using the up and down keys
|
||||
|
||||
## Usage
|
||||
Include the JavaScript file:
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="path_to_plugins/chatrecall/candy.js"></script>
|
||||
```
|
||||
|
||||
Call its `init()` method after Candy has been initialized:
|
||||
|
||||
```JavaScript
|
||||
Candy.init('/http-bind/');
|
||||
|
||||
CandyShop.ChatRecall.init();
|
||||
|
||||
Candy.Core.connect();
|
||||
```
|
||||
|
||||
## Configuration options
|
||||
`messagesToKeep` - Integer - The number of messages to store in history. Defaults to 10
|
||||
|
||||
## Example configurations
|
||||
```JavaScript
|
||||
// Store 25 messages for the user to scroll through
|
||||
CandyShop.ChatRecall.init({
|
||||
messagesToKeep: 25
|
||||
});
|
||||
```
|
@ -0,0 +1,116 @@
|
||||
/** File: candy.js
|
||||
* Candy - Chats are not dead yet.
|
||||
*
|
||||
* Authors:
|
||||
* - Troy McCabe <troy.mccabe@geeksquad.com>
|
||||
*
|
||||
* Copyright:
|
||||
* (c) 2012 Geek Squad. All rights reserved.
|
||||
*/
|
||||
|
||||
/* global document, Candy, jQuery */
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
/** Class: CandyShop.ChatRecall
|
||||
* Remembers the last {x} messages the user types and allows up and down key recollection
|
||||
*/
|
||||
CandyShop.ChatRecall = (function(self, Candy, $) {
|
||||
/** Object: _options
|
||||
* Options:
|
||||
* (Integer) messagesToKeep - How many messages to keep in memory
|
||||
*/
|
||||
var _options = {
|
||||
messagesToKeep: 10
|
||||
};
|
||||
|
||||
/** Array: _messages
|
||||
* The messages that the user sent
|
||||
*/
|
||||
var _messages = [];
|
||||
|
||||
/** Integer: _currentMessageIndex
|
||||
* The current index of the message the user went back to
|
||||
*/
|
||||
var _currentMessageIndex = 0;
|
||||
|
||||
/** Function: init
|
||||
* Initialize the ChatRecall plugin
|
||||
*
|
||||
* Parameters:
|
||||
* (Object) options - An options packet to apply to this plugin
|
||||
*/
|
||||
self.init = function(options) {
|
||||
// apply the supplied options to the defaults specified
|
||||
$.extend(true, _options, options);
|
||||
|
||||
// Listen for keydown in the field
|
||||
$(document).on('keydown', 'input[name="message"]', function(e) {
|
||||
// switch on the key code
|
||||
switch (e.which) {
|
||||
// up arrow
|
||||
case 38:
|
||||
// if we're under the cap of max messages and the cap of the messages currently stored, recall
|
||||
if (_currentMessageIndex < _options.messagesToKeep && _currentMessageIndex < _messages.length) {
|
||||
// if we're at blank (the bottom), move it up to 0
|
||||
if (_currentMessageIndex === -1) {
|
||||
_currentMessageIndex++;
|
||||
}
|
||||
// set the value to what we stored
|
||||
$(this).val(_messages[_currentMessageIndex]);
|
||||
// if we're under the limits, go ahead and move the tracked position up
|
||||
if (_currentMessageIndex < _options.messagesToKeep - 1 && _currentMessageIndex < _messages.length - 1) {
|
||||
_currentMessageIndex++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// down arrow
|
||||
case 40:
|
||||
// if we're back to the bottom, clear the field
|
||||
// else move it down
|
||||
if (_currentMessageIndex === -1) {
|
||||
$(this).val('');
|
||||
} else {
|
||||
// if we're at the cap already, move it down initially (don't want to have to hit it twice)
|
||||
if (_currentMessageIndex === _options.messagesToKeep - 1 || _currentMessageIndex === _messages.length - 1) {
|
||||
_currentMessageIndex--;
|
||||
}
|
||||
// set the value to the one that's stored
|
||||
$(this).val(_messages[_currentMessageIndex]);
|
||||
|
||||
if (_currentMessageIndex > -1) {
|
||||
// move the tracked position down
|
||||
_currentMessageIndex--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// listen before send and add it to the stack
|
||||
$(Candy).on('candy:view.message.before-send', function(e, data) {
|
||||
// remove, in case there is the colors plugin, the |c:number| prefix
|
||||
self.addMessage(data.message.replace(/\|c:\d+\|/i, ''));
|
||||
});
|
||||
};
|
||||
|
||||
/** Function: addMessage
|
||||
* Add a message to the front of the stack
|
||||
* This is stored New[0] -> Old[N]
|
||||
*
|
||||
* Parameters:
|
||||
* (String) message - The message to store
|
||||
*/
|
||||
self.addMessage = function(message) {
|
||||
// pop one off the end if it's too many
|
||||
if (_messages.length === _options.messagesToKeep) {
|
||||
_messages.pop();
|
||||
}
|
||||
|
||||
// put the message at pos 0 and move everything else
|
||||
_messages.unshift(message);
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.ChatRecall || {}, Candy, jQuery));
|
@ -0,0 +1,43 @@
|
||||
# Colors using XHTML formatted messages
|
||||
Send and receive colored messages.
|
||||
This plugin is based on the [colors](https://github.com/candy-chat/candy-plugins/tree/master/colors) and, contrary
|
||||
to the `colors` plugin, ensures that third party clients see the colors as well.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
To enable *Colors* you have to include its JavaScript code and stylesheet
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/colors-xhtml/candy.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="candyshop/colors-xhtml/candy.css" />
|
||||
```
|
||||
|
||||
Call its `init()` method after Candy has been initialized:
|
||||
|
||||
```javascript
|
||||
Candy.init('/http-bind/', {
|
||||
view: {
|
||||
// make sure you enabled XHTML in order to display it properly.
|
||||
enableXHTML: true
|
||||
}
|
||||
});
|
||||
|
||||
// enable Colors-XHTML plugin (default: 8 colors)
|
||||
CandyShop.ColorsXhtml.init();
|
||||
|
||||
Candy.Core.connect();
|
||||
```
|
||||
|
||||
To enable less or more colors enable it with e.g.:
|
||||
|
||||
```javascript
|
||||
var colors = [
|
||||
'#333', '#fff'
|
||||
// etc, use as many as you'd like to
|
||||
];
|
||||
|
||||
CandyShop.ColorsXhtml.init(colors);
|
||||
```
|
||||
|
||||
**Please note**: you can't use the `colors` and the `colors-xhtml` plugin together.
|
@ -0,0 +1,33 @@
|
||||
#colors-control {
|
||||
background: no-repeat url('colors-control.png');
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#colors-control-indicator {
|
||||
display: inline-block;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
border: 1px solid white;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 100%;
|
||||
margin: -8px 0 0 -8px;
|
||||
}
|
||||
|
||||
#context-menu .colors {
|
||||
padding-left: 5px;
|
||||
width: 89px;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#context-menu .colors:hover {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
#context-menu .colors span {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1px solid white;
|
||||
margin: 3px;
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/* global Candy, jQuery */
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
CandyShop.ColorsXhtml = (function(self, Candy, $) {
|
||||
|
||||
var _numColors,
|
||||
_currentColor = '',
|
||||
_colors = [
|
||||
'#333',
|
||||
'#c4322b',
|
||||
'#37991e',
|
||||
'#1654c9',
|
||||
'#66379b',
|
||||
'#ba7318',
|
||||
'#32938a',
|
||||
'#9e2274'
|
||||
];
|
||||
|
||||
self.init = function(colors) {
|
||||
if(colors && colors.length) {
|
||||
_colors = colors;
|
||||
}
|
||||
_numColors = _colors.length;
|
||||
|
||||
self.applyTranslations();
|
||||
|
||||
$(Candy).on('candy:view.message.before-send', function(e, args) {
|
||||
if(_currentColor !== '' && $.trim(args.message) !== '') {
|
||||
args.xhtmlMessage = '<span style="color:' + _currentColor + '">' + Candy.Util.Parser.escape(args.message) + '</span>';
|
||||
}
|
||||
});
|
||||
|
||||
if(Candy.Util.cookieExists('candyshop-colors-xhtml-current')) {
|
||||
var color = Candy.Util.getCookie('candyshop-colors-xhtml-current');
|
||||
if(_colors.indexOf(color) !== -1) {
|
||||
_currentColor = color;
|
||||
}
|
||||
}
|
||||
var html = '<li id="colors-control" data-tooltip="' + $.i18n._('candyshopColorsXhtmlMessagecolor') + '"><span style="color:' + _currentColor + ';background-color:' + _currentColor +'" id="colors-control-indicator"></span></li>';
|
||||
$('#emoticons-icon').after(html);
|
||||
$('#colors-control').click(function() {
|
||||
CandyShop.ColorsXhtml.showPicker(this);
|
||||
});
|
||||
};
|
||||
|
||||
self.showPicker = function(elem) {
|
||||
elem = $(elem);
|
||||
var pos = elem.offset(),
|
||||
menu = $('#context-menu'),
|
||||
content = $('ul', menu),
|
||||
colors = '',
|
||||
i;
|
||||
|
||||
$('#tooltip').hide();
|
||||
|
||||
for(i = _numColors-1; i >= 0; i--) {
|
||||
colors = '<span style="color:' + _colors[i] + ';background-color:' + _colors[i] + ';" data-color="' + _colors[i] + '"></span>' + colors;
|
||||
}
|
||||
content.html('<li class="colors">' + colors + '</li>');
|
||||
content.find('span').click(function() {
|
||||
_currentColor = $(this).attr('data-color');
|
||||
$('#colors-control-indicator').attr('style', 'color:' + _currentColor + ';background-color:' + _currentColor);
|
||||
Candy.Util.setCookie('candyshop-colors-xhtml-current', _currentColor, 365);
|
||||
Candy.View.Pane.Room.setFocusToForm(Candy.View.getCurrent().roomJid);
|
||||
menu.hide();
|
||||
});
|
||||
|
||||
var posLeft = Candy.Util.getPosLeftAccordingToWindowBounds(menu, pos.left),
|
||||
posTop = Candy.Util.getPosTopAccordingToWindowBounds(menu, pos.top);
|
||||
|
||||
menu.css({'left': posLeft.px, 'top': posTop.px, backgroundPosition: posLeft.backgroundPositionAlignment + ' ' + posTop.backgroundPositionAlignment});
|
||||
menu.fadeIn('fast');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
self.applyTranslations = function() {
|
||||
var translations = {
|
||||
'en' : 'Message Color',
|
||||
'ru' : 'Цвет сообщения',
|
||||
'de' : 'Farbe für Nachrichten',
|
||||
'fr' : 'Couleur des messages',
|
||||
'nl' : 'Berichtkleur',
|
||||
'es' : 'Color de los mensajes'
|
||||
};
|
||||
$.each(translations, function(k, v) {
|
||||
if(Candy.View.Translation[k]) {
|
||||
Candy.View.Translation[k].candyshopColorsXhtmlMessagecolor = v;
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.ColorsXhtml || {}, Candy, jQuery));
|
Binary file not shown.
After Width: | Height: | Size: 816 B |
@ -0,0 +1,29 @@
|
||||
# Colors
|
||||
Send and receive colored messages.
|
||||
|
||||
This plugin uses an own format of messages (`foobar` becomes e.g. `|c:1|foobar`).
|
||||
If you'd like to use XHTML for formatting messages (this ensures third-party clients could also
|
||||
display it properly), please use [colors-html](https://github.com/candy-chat/candy-plugins/tree/master/colors-xhtml).
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
To enable *Colors* you have to include its JavaScript code and stylesheet
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/colors/candy.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="candyshop/colors/candy.css" />
|
||||
```
|
||||
|
||||
Call its `init()` method after Candy has been initialized:
|
||||
|
||||
```JavaScript
|
||||
Candy.init('/http-bind/');
|
||||
|
||||
// enable Colors plugin (default: 8 colors)
|
||||
CandyShop.Colors.init();
|
||||
|
||||
Candy.Core.connect();
|
||||
```
|
||||
|
||||
To enable less or more colors just call `CandyShop.Colors.init(<number-of-colors>)`.
|
@ -0,0 +1,97 @@
|
||||
#colors-control {
|
||||
background: no-repeat url('colors-control.png');
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#colors-control-indicator {
|
||||
display: inline-block;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
border: 1px solid white;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 100%;
|
||||
margin: -8px 0 0 -8px;
|
||||
}
|
||||
|
||||
#context-menu .colors {
|
||||
padding-left: 5px;
|
||||
width: 89px;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#context-menu .colors:hover {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
#context-menu .colors span {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1px solid white;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.message-pane span.colored {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.color-0 {
|
||||
color: #333;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.color-1 {
|
||||
color: #c4322b;
|
||||
background-color: #c4322b;
|
||||
}
|
||||
|
||||
.color-2 {
|
||||
color: #37991e;
|
||||
background-color: #37991e;
|
||||
}
|
||||
|
||||
.color-3 {
|
||||
color: #1654c9;
|
||||
background-color: #1654c9;
|
||||
}
|
||||
|
||||
.color-4 {
|
||||
color: #66379b;
|
||||
background-color: #66379b;
|
||||
}
|
||||
|
||||
.color-5 {
|
||||
color: #ba7318;
|
||||
background-color: #ba7318;
|
||||
}
|
||||
|
||||
.color-6 {
|
||||
color: #32938a;
|
||||
background-color: #32938a;
|
||||
}
|
||||
|
||||
.color-7 {
|
||||
color: #9e2274;
|
||||
background-color: #9e2274;
|
||||
}
|
||||
|
||||
.color-8 {
|
||||
color: #4C82E4;
|
||||
background-color: #4C82E4;
|
||||
}
|
||||
|
||||
.color-9 {
|
||||
color: #7F140E;
|
||||
background-color: #7F140E;
|
||||
}
|
||||
|
||||
.color-10 {
|
||||
color: #1C630A;
|
||||
background-color: #1C630A;
|
||||
}
|
||||
|
||||
.color-11 {
|
||||
color: #CF55A4;
|
||||
background-color: #CF55A4;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/* global Candy, jQuery */
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
CandyShop.Colors = (function(self, Candy, $) {
|
||||
|
||||
var _numColors,
|
||||
_currentColor = 0;
|
||||
|
||||
self.init = function(numColors) {
|
||||
_numColors = numColors ? numColors : 8;
|
||||
|
||||
self.applyTranslations();
|
||||
|
||||
$(Candy).on('candy:view.message.before-send', function(e, args) {
|
||||
if(_currentColor > 0 && $.trim(args.message) !== '') {
|
||||
args.message = '|c:'+ _currentColor +'|' + args.message;
|
||||
}
|
||||
});
|
||||
|
||||
$(Candy).on('candy:view.message.before-render', function(e, args) {
|
||||
args.templateData.message = args.templateData.message.replace(/^\|c:([0-9]{1,2})\|(.*)/gm, '<span class="colored color-$1">$2</span>');
|
||||
});
|
||||
|
||||
if(Candy.Util.cookieExists('candyshop-colors-current')) {
|
||||
var color = parseInt(Candy.Util.getCookie('candyshop-colors-current'), 10);
|
||||
if(color > 0 && color < _numColors) {
|
||||
_currentColor = color;
|
||||
}
|
||||
}
|
||||
var html = '<li id="colors-control" data-tooltip="' + $.i18n._('candyshopColorsMessagecolor') + '"><span class="color-' + _currentColor + '" id="colors-control-indicator"></span></li>';
|
||||
$('#emoticons-icon').after(html);
|
||||
$('#colors-control').click(function() {
|
||||
CandyShop.Colors.showPicker(this);
|
||||
});
|
||||
};
|
||||
|
||||
self.showPicker = function(elem) {
|
||||
elem = $(elem);
|
||||
var pos = elem.offset(),
|
||||
menu = $('#context-menu'),
|
||||
content = $('ul', menu),
|
||||
colors = '',
|
||||
i;
|
||||
|
||||
$('#tooltip').hide();
|
||||
|
||||
for(i = _numColors-1; i >= 0; i--) {
|
||||
colors = '<span class="color-' + i + '" data-color="' + i + '"></span>' + colors;
|
||||
}
|
||||
content.html('<li class="colors">' + colors + '</li>');
|
||||
content.find('span').click(function() {
|
||||
_currentColor = $(this).attr('data-color');
|
||||
$('#colors-control-indicator').attr('class', 'color-' + _currentColor);
|
||||
Candy.Util.setCookie('candyshop-colors-current', _currentColor, 365);
|
||||
Candy.View.Pane.Room.setFocusToForm(Candy.View.getCurrent().roomJid);
|
||||
menu.hide();
|
||||
});
|
||||
|
||||
var posLeft = Candy.Util.getPosLeftAccordingToWindowBounds(menu, pos.left),
|
||||
posTop = Candy.Util.getPosTopAccordingToWindowBounds(menu, pos.top);
|
||||
|
||||
menu.css({'left': posLeft.px, 'top': posTop.px, backgroundPosition: posLeft.backgroundPositionAlignment + ' ' + posTop.backgroundPositionAlignment});
|
||||
menu.fadeIn('fast');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
self.applyTranslations = function() {
|
||||
var translations = {
|
||||
'en' : 'Message Color',
|
||||
'ru' : 'Цвет сообщения',
|
||||
'de' : 'Farbe für Nachrichten',
|
||||
'fr' : 'Couleur des messages',
|
||||
'nl' : 'Berichtkleur',
|
||||
'es' : 'Color de los mensajes'
|
||||
};
|
||||
$.each(translations, function(k, v) {
|
||||
if(Candy.View.Translation[k]) {
|
||||
Candy.View.Translation[k].candyshopColorsMessagecolor = v;
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.Colors || {}, Candy, jQuery));
|
Binary file not shown.
After Width: | Height: | Size: 816 B |
@ -0,0 +1,78 @@
|
||||
# Emphasis
|
||||
|
||||
Basic message formatting, with xhtml conversion, compatible with XEP-0071. Standard messages are converted to Textile-style formatting.
|
||||
|
||||
Textile, BBCode, and Html Variants are supported:
|
||||
|
||||
**bold**
|
||||
```
|
||||
*bold*
|
||||
[b]bold[/b]
|
||||
<b>bold</b>
|
||||
<strong>bold</strong>
|
||||
```
|
||||
|
||||
_italic_
|
||||
```
|
||||
_italic_
|
||||
[i]italic[/i]
|
||||
<i>italic</i>
|
||||
<em>italic</em>
|
||||
```
|
||||
|
||||
<ins>underlined</ins>
|
||||
```
|
||||
+underlined+
|
||||
[u]underlined[/u]
|
||||
<u>underlined</u>
|
||||
<ins>underlined</ins>
|
||||
```
|
||||
|
||||
~~strikethrough~~
|
||||
```
|
||||
-strikethrough-
|
||||
[s]strikethrough[/s]
|
||||
<s>strinkethough</s>
|
||||
<del>strikethrough</del>
|
||||
```
|
||||
|
||||
Textile can be escaped like so:
|
||||
|
||||
```
|
||||
==-strikethrough-==
|
||||
```
|
||||
|
||||
This plugin is compatible with colors-xhtml.
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
Include the JavaScript file:
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/emphasis/candy.js"></script>
|
||||
```
|
||||
|
||||
Call its `init()` method after Candy has been initialized:
|
||||
|
||||
```javascript
|
||||
Candy.init('/http-bind/', {});
|
||||
|
||||
// enable basic textile/BBCode/Html handling
|
||||
CandyShop.Emphasis.init();
|
||||
|
||||
Candy.Core.connect();
|
||||
```
|
||||
|
||||
Optionally, different formats can be disabled.
|
||||
|
||||
|
||||
```javascript
|
||||
CandyShop.Emphasis.init({ textile: false, bbcode: true, html: true });
|
||||
```
|
||||
|
||||
Or just
|
||||
|
||||
```javascript
|
||||
CandyShop.Emphasis.init({ textile: false });
|
||||
```
|
@ -0,0 +1,86 @@
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
CandyShop.Emphasis = (function(self, Candy, $) {
|
||||
|
||||
// textile, bbcode, old html, escaped old html, new html, escaped new html
|
||||
var _styles = {
|
||||
textile: [
|
||||
{ plain: '==*bold*==', regex: /((^|\s|\>)==\*(.*?)\*==(\s|\<|$))/gm, plain: "$2*$3*$4", xhtml: "$2*$3*$4" },
|
||||
{ plain: '==_italic_==', regex: /((^|\s|\>)==\_(.*?)\_==(\s|\<|$))/gm, plain: "$2_$3_$4", xhtml: "$2_$3_$4" },
|
||||
{ plain: '==-strikethrough-==', regex: /((^|\s|\>)==\-(.*?)\-==(\s|\<|$))/gm, plain: "$2-$3-$4", xhtml: "$2-$3-$4" },
|
||||
{ plain: '==+underline+==', regex: /((^|\s|\>)==\+(.*?)\+==(\s|\<|$))/gm, plain: "$2+$3+$4", xhtml: "$2+$3+$4" },
|
||||
{ plain: '*bold*', regex: /((^|\s|\>)\*(.*?)\*(\s|\<|$))/gm, plain: "$2*$3*$4", xhtml: "$2<strong>$3</strong>$4" },
|
||||
{ plain: '_italic_', regex: /((^|\s|\>)\_(.*?)\_(\s|\<|$))/gm, plain: "$2_$3_$4", xhtml: "$2<em>$3</em>$4" },
|
||||
{ plain: '-strikethrough-', regex: /((^|\s|\>)\-(.*?)\-(\s|\<|$))/gm, plain: "$2-$3-$4", xhtml: "$2<span style='text-decoration: line-through;'>$3</span>$4" },
|
||||
{ plain: '+underline+', regex: /((^|\s|\>)\+(.*?)\+(\s|\<|$))/gm, plain: "$2+$3+$4", xhtml: "$2<span style='text-decoration: underline;'>$3</span>$4" }
|
||||
],
|
||||
bbcode: [
|
||||
{ plain: '[b]bold[/b]', regex: /(\[b\](.*?)\[\/b\])/igm, plain: "*$2*", xhtml: "<strong>$2</strong>" },
|
||||
{ plain: '[i]italic[/i]', regex: /(\[i\](.*?)\[\/i\])/igm, plain: "_$2_", xhtml: "<em>$2</em>" },
|
||||
{ plain: '[s]strikethrough[/s]', regex: /(\[s\](.*?)\[\/s\])/igm, plain: "-$2-", xhtml: "<span style='text-decoration: line-through;'>$2</span>" },
|
||||
{ plain: '[u]underline[/u]', regex: /(\[u\](.*?)\[\/u\])/igm, plain: "+$2+", xhtml: "<span style='text-decoration: underline;'>$2</span>" }
|
||||
],
|
||||
html: [
|
||||
//handling both escaped an unescaped, just in case.
|
||||
{ plain: '<b>bold</b>', regex: /(\<b\>(.*?)\<\/b\>)/igm, plain: "*$2*", xhtml: "<strong>$2</strong>" },
|
||||
{ plain: '<strong>bold</strong>', regex: /(\<strong\>(.*?)\<\/strong\>)/igm, plain: "*$2*", xhtml: "<strong>$2</strong>" },
|
||||
{ plain: '<i>italic</i>', regex: /(\<i\>(.*?)\<\/i\>)/igm, plain: "_$2_", xhtml: "<em>$2</em>" },
|
||||
{ plain: '<em>italic</em>', regex: /(\<em\>(.*?)\<\/em\>)/igm, plain: "_$2_", xhtml: "<em>$2</em>" },
|
||||
{ plain: '<s>strikethrough</s>', regex: /(\<s\>(.*?)\<\/s\>)/igm, plain: "-$2-", xhtml: "<span style='text-decoration: line-through;'>$2</span>" },
|
||||
{ plain: '<del>strikethrough</del>', regex: /(\<del\>(.*?)\<\/del\>)/igm, plain: "-$2-", xhtml: "<span style='text-decoration: line-through;'>$2</span>" },
|
||||
{ plain: '<u>underline</u>', regex: /(\<u\>(.*?)\<\/u\>)/igm, plain: "+$2+", xhtml: "<span style='text-decoration: underline;'>$2</span>" },
|
||||
{ plain: '<ins>underline</ins>', regex: /(\<ins\>(.*?)\<\/ins\>)/igm, plain: "+$2+", xhtml: "<span style='text-decoration: underline;'>$2</span>" },
|
||||
{ plain: '<b>bold</b>', regex: /(\<b\>(.*?)\<\/b\>)/igm, plain: "*$2*", xhtml: "<strong>$2</strong>" },
|
||||
{ plain: '<strong>bold</strong>', regex: /(\<strong\>(.*?)\<\/strong\>)/igm, plain: "*$2*", xhtml: "<strong>$2</strong>" },
|
||||
{ plain: '<i>italic</i>', regex: /(\<i\>(.*?)\<\/i\>)/igm, plain: "_$2_", xhtml: "<em>$2</em>" },
|
||||
{ plain: '<em>italic</em>', regex: /(\<em\>(.*?)\<\/em\>)/igm, plain: "_$2_", xhtml: "<em>$2</em>" },
|
||||
{ plain: '<s>strikethrough</s>', regex: /(\<s\>(.*?)\<\/s\>)/igm, plain: "-$2-", xhtml: "<span style='text-decoration: line-through;'>$2</span>" },
|
||||
{ plain: '<del>strikethrough</del>', regex: /(\<del\>(.*?)\<\/del\>)/igm, plain: "-$2-", xhtml: "<span style='text-decoration: line-through;'>$2</span>" },
|
||||
{ plain: '<u>underline</u>', regex: /(\<u\>(.*?)\<\/u\>)/igm, plain: "+$2+", xhtml: "<span style='text-decoration: underline;'>$2</span>" },
|
||||
{ plain: '<ins>underline</ins>', regex: /(\<ins\>(.*?)\<\/ins\>)/igm, plain: "+$2+", xhtml: "<span style='text-decoration: underline;'>$2</span>" }
|
||||
|
||||
]
|
||||
};
|
||||
|
||||
var _options = {
|
||||
textile: true,
|
||||
bbcode: true,
|
||||
html: true
|
||||
};
|
||||
|
||||
self.init = function( options ) {
|
||||
// apply the supplied options to the defaults specified
|
||||
$.extend( true, _options, options );
|
||||
|
||||
$(Candy).on( 'candy:view.message.before-send', function(e, args) {
|
||||
var workingPlainMessage = args.message;
|
||||
var workingXhtmlMessage = args.message;
|
||||
|
||||
if( args.xhtmlMessage ) {
|
||||
var workingXhtmlMessage = args.xhtmlMessage;
|
||||
}
|
||||
|
||||
$.each( _options, function( optionIndex, optionValue ){
|
||||
if( optionValue === true ){
|
||||
$.each( _styles[optionIndex], function( styleIndex, styleValue ){
|
||||
workingPlainMessage = workingPlainMessage.replace( styleValue.regex, styleValue.plain );
|
||||
workingXhtmlMessage = workingXhtmlMessage.replace( styleValue.regex, styleValue.xhtml );
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if( workingPlainMessage != workingXhtmlMessage) {
|
||||
// strophe currently requires that xhtml have a root element. Apparently.
|
||||
// Force one node, with no external text
|
||||
if( !workingXhtmlMessage.match( /^<.*>$/ ) || $( workingXhtmlMessage ).length != 1 ) {
|
||||
workingXhtmlMessage = "<span>" + workingXhtmlMessage + "</span>";
|
||||
}
|
||||
args.message = workingPlainMessage;
|
||||
args.xhtmlMessage = workingXhtmlMessage;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.Emphasis || {}, Candy, jQuery));
|
@ -0,0 +1,24 @@
|
||||
# Left Tabs + Bootstrap3 Icons
|
||||
|
||||
A plugin for Candy Chat to enable left tabs with simple Bootstrap3 theme elements.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
Include the JavaScript and CSS files:
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/lefttabs/lefttabs.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="candyshop/lefttabs/lefttabs.css" />
|
||||
```
|
||||
Remember to include your Bootstrap3 CSS/JS files! They are not included in this plugin. ;)
|
||||
|
||||
To enable this Left Tabs plugin, add its `init` method _before_ you `init` Candy:
|
||||
```JavaScript
|
||||
CandyShop.LeftTabs.init();
|
||||
Candy.init('/http-bind', { ...
|
||||
```
|
||||
## Compatibility with other plugins
|
||||
|
||||
Make sure to `init` it after all other plugins, but before the Candy `init`.
|
||||
|
||||
1. CreateRoom
|
@ -0,0 +1,239 @@
|
||||
/**
|
||||
* LeftTabs CSS
|
||||
*
|
||||
* @author Melissa Adamaitis <melissa@melissanoelle.com>
|
||||
*/
|
||||
|
||||
/* Message pane/body CSS. */
|
||||
#chat-rooms {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin-left: 50%;
|
||||
margin-right: 14.6%;
|
||||
width: 30.5%;
|
||||
}
|
||||
.message-pane-wrapper {
|
||||
float:right;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.message-pane {
|
||||
height: 100%;
|
||||
padding-top: 1px;
|
||||
width: 69.8%;
|
||||
overflow-y: scroll;
|
||||
position: fixed;
|
||||
}
|
||||
.message-pane .label {
|
||||
padding-top: 7px;
|
||||
}
|
||||
|
||||
/* Input form CSS. */
|
||||
.message-form-wrapper {
|
||||
float: left;
|
||||
margin-right:auto;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.message-form {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.message-form input.submit {
|
||||
margin: 2px 3px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* Roster (right) menu CSS. */
|
||||
.roster-pane {
|
||||
background-color: initial;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
float:right;
|
||||
margin: 0;
|
||||
padding-right: 3px;
|
||||
padding-top: 2px;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
width: 14%;
|
||||
}
|
||||
.roster-pane .label {
|
||||
color: #555;
|
||||
font-size: 0.85em;
|
||||
line-height: 1em;
|
||||
padding-left: 0;
|
||||
text-shadow: none;
|
||||
width: auto;
|
||||
}
|
||||
.roster-pane .user {
|
||||
border: 0;
|
||||
box-sizing: initial;
|
||||
box-shadow: none;
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
}
|
||||
.roster-pane .user ul {
|
||||
float: right;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
.roster-pane .user:hover {
|
||||
background-color: initial;
|
||||
}
|
||||
.roster-pane .user:hover .label {
|
||||
color: #33bbfc;
|
||||
}
|
||||
|
||||
/* Toolbar CSS. (Below roster.) */
|
||||
#chat-toolbar {
|
||||
height: 30px;
|
||||
margin-bottom: 0;
|
||||
width: 14.5%;
|
||||
}
|
||||
#chat-toolbar li {
|
||||
background-image: none !important;
|
||||
font-size: 1.25em;
|
||||
line-height: 1em;
|
||||
}
|
||||
#emoticons-icon {
|
||||
color: #F3E43C;
|
||||
}
|
||||
/* Volume. */
|
||||
#chat-sound-control, #chat-autoscroll-control {
|
||||
color: #ccc;
|
||||
}
|
||||
#chat-sound-control .glyphicon {
|
||||
display: none;
|
||||
}
|
||||
#chat-sound-control .glyphicon.glyphicon-volume-off {
|
||||
display: initial;
|
||||
color: #9b1414;
|
||||
}
|
||||
#chat-sound-control.checked .glyphicon.glyphicon-volume-off {
|
||||
display: none;
|
||||
}
|
||||
#chat-sound-control.checked .glyphicon.glyphicon-volume-up {
|
||||
display: block;
|
||||
}
|
||||
/* Scroll */
|
||||
#chat-autoscroll-control {
|
||||
position: relative;
|
||||
}
|
||||
#chat-autoscroll-control .glyphicon.glyphicon-ban-circle {
|
||||
display: initial;
|
||||
color: #9b1414;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
#chat-autoscroll-control.checked .glyphicon.glyphicon-ban-circle {
|
||||
display: none;
|
||||
}
|
||||
/* Status message */
|
||||
#chat-statusmessage-control {
|
||||
position: relative;
|
||||
color: #6EAEFF;
|
||||
}
|
||||
#chat-statusmessage-control .glyphicon.glyphicon-ban-circle {
|
||||
display: initial;
|
||||
color: #9b1414;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
}
|
||||
#chat-statusmessage-control.checked .glyphicon.glyphicon-ban-circle {
|
||||
display: none;
|
||||
}
|
||||
/* Users icon */
|
||||
.usercount span.glyphicon {
|
||||
background-color: initial;
|
||||
}
|
||||
|
||||
/* Left menu CSS. */
|
||||
#left-menu-wrapper {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
position: fixed;
|
||||
width: 50%;
|
||||
}
|
||||
#chat-tabs {
|
||||
margin: 0;
|
||||
float: right;
|
||||
width: 99%;
|
||||
}
|
||||
#chat-tabs > li {
|
||||
margin: 0;
|
||||
margin-top: 2px;
|
||||
width: 100% !important;
|
||||
}
|
||||
#chat-tabs > li:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
#chat-tabs a.transition {
|
||||
display: none;
|
||||
}
|
||||
#chat-tabs a.label {
|
||||
border-radius: 3px 0 0 3px;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
#chat-tabs a.close {
|
||||
right: -5px;
|
||||
top: -7px;
|
||||
}
|
||||
|
||||
/* Extra details (badges, non-specific hovers, background colors, etc...) */
|
||||
#chat-tabs small.unread {
|
||||
border-radius: 50%;
|
||||
cursor: default;
|
||||
height: 17px;
|
||||
padding: 0;
|
||||
top: 3px;
|
||||
text-align: center;
|
||||
width: 17px;
|
||||
}
|
||||
.label[href]:hover, .label[href]:focus {
|
||||
color: #858585;
|
||||
}
|
||||
#chat-pane, #roster-pane {
|
||||
background: #b0e1f2; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #b0e1f2 26%, #81bfe2 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(26%,#b0e1f2), color-stop(100%,#81bfe2)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #b0e1f2 26%,#81bfe2 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #b0e1f2 26%,#81bfe2 100%); /* Opera 11.10+ */
|
||||
background: -ms-linear-gradient(top, #b0e1f2 26%,#81bfe2 100%); /* IE10+ */
|
||||
background: linear-gradient(to bottom, #b0e1f2 26%,#81bfe2 100%); /* W3C */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b0e1f2', endColorstr='#81bfe2',GradientType=0 ); /* IE6-9 */
|
||||
}
|
||||
|
||||
/* Compatibility with CreateRoom plugin. */
|
||||
#create-group {
|
||||
background: #eee;
|
||||
border-radius: 3px 0 0 3px;
|
||||
cursor: pointer !important;
|
||||
float: right;
|
||||
height: 18px !important;
|
||||
margin-right: 1px;
|
||||
margin-top: 9px;
|
||||
position: initial;
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
#create-group .click {
|
||||
font-size: 75%;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
vertical-align: baseline;
|
||||
position: initial;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* Align tooltip context menu properly in the roster. */
|
||||
#context-menu {
|
||||
margin-top: 48px !important;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
# /me Does
|
||||
|
||||
Format /me messages, implementing XEP-0245
|
||||
|
||||
## Usage
|
||||
Include the JavaScript file:
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/me-does/candy.js"></script>
|
||||
```
|
||||
|
||||
Call its `init()` method after Candy has been initialized:
|
||||
|
||||
```javascript
|
||||
Candy.init('/http-bind/', {});
|
||||
|
||||
// enable /me handling
|
||||
CandyShop.MeDoes.init();
|
||||
|
||||
Candy.Core.connect();
|
||||
```
|
||||
|
||||
Now all messages starting with '/me 'will use infoMessage formatting.
|
||||
|
||||
```
|
||||
/me takes screenshot
|
||||
```
|
||||
|
||||

|
||||
|
||||
**Please note**: As `me-does` reroutes message output, it's call to `init()` should happen after the `init()` of most other plugins, including, `inline-images`.
|
@ -0,0 +1,47 @@
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
CandyShop.MeDoes = (function(self, Candy, $) {
|
||||
// CandyShop.Timeago
|
||||
|
||||
|
||||
self.init = function() {
|
||||
$(Candy).on("candy:view.message.before-show", function(e, args) {
|
||||
if (args && args.message && args.message.match(/^\/me /i)) {
|
||||
var message = args.message.match(/^\/([^\s]+)(?:\s+(.*))?$/m)[2];
|
||||
self.userInfoMessage(args.roomJid, args.name, message);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
if(CandyShop.Timeago === undefined) {
|
||||
Candy.View.Template.Chat.userInfoMessage = '<li><small>{{time}}</small><div class="infomessage">' +
|
||||
'<span class="spacer">•</span> <span><strong>{{name}}</strong> {{{message}}}</span></div></li>';
|
||||
}
|
||||
else {
|
||||
Candy.View.Template.Chat.userInfoMessage = '<li><small><abbr title="{{time}}">{{time}}</abbr></small><div class="infomessage">' +
|
||||
'<span class="spacer">•</span> <span><strong>{{name}}</strong> {{{message}}}</span></div></li>';
|
||||
}
|
||||
|
||||
//Using logic from real infoMessage function and inserting custom template
|
||||
self.userInfoMessage = function (roomJid, name, message){
|
||||
|
||||
if(Candy.View.getCurrent().roomJid) {
|
||||
var html = Mustache.to_html(Candy.View.Template.Chat.userInfoMessage, {
|
||||
name: name,
|
||||
message: message,
|
||||
time: Candy.Util.localizedTime(new Date().toGMTString())
|
||||
});
|
||||
Candy.View.Pane.Room.appendToMessagePane(roomJid, html);
|
||||
if (Candy.View.getCurrent().roomJid === roomJid) {
|
||||
Candy.View.Pane.Room.scrollToBottom(Candy.View.getCurrent().roomJid);
|
||||
}
|
||||
$(Candy).triggerHandler('candy:view.message.after-show', {
|
||||
'message' : message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.MeDoes || {}, Candy, jQuery));
|
@ -0,0 +1,29 @@
|
||||
# Modify role
|
||||
Adds **add moderator** and **remove moderator** privilege links to context menu.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
To enable *Modify role* you have to include its JavaScript code and stylesheet:
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/modify-role/candy.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="candyshop/modify-role/candy.css" />
|
||||
```
|
||||
|
||||
Call its `init()` method after Candy has been initialized:
|
||||
|
||||
```JavaScript
|
||||
Candy.init('/http-bind/');
|
||||
|
||||
// enable ModifyRole plugin
|
||||
CandyShop.ModifyRole.init();
|
||||
|
||||
Candy.Core.connect();
|
||||
```
|
||||
|
||||
## Credits
|
||||
Thanks to [famfamfam silk icons](http://www.famfamfam.com/lab/icons/silk/) for the icons.
|
||||
|
||||
## License
|
||||
MIT
|
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
@ -0,0 +1,6 @@
|
||||
#context-menu .add-moderator {
|
||||
background-image: url(add-moderator.png);
|
||||
}
|
||||
#context-menu .remove-moderator {
|
||||
background-image: url(remove-moderator.png);
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/** File: candy.js
|
||||
* Plugin for modifying roles. Currently implemented: op & deop
|
||||
*
|
||||
* Authors:
|
||||
* - Michael Weibel <michael.weibel@gmail.com>
|
||||
*
|
||||
* License: MIT
|
||||
*
|
||||
* Copyright:
|
||||
* (c) 2014 Michael Weibel. All rights reserved.
|
||||
*/
|
||||
|
||||
/* global Candy, jQuery, Strophe, $iq */
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
/** Class: CandyShop.ModifyRole
|
||||
* Remove the ignore option in the roster
|
||||
*/
|
||||
CandyShop.ModifyRole = (function(self, Candy, $) {
|
||||
|
||||
var modifyRole = function modifyRole(role, roomJid, user) {
|
||||
var conn = Candy.Core.getConnection(),
|
||||
nick = user.getNick(),
|
||||
iq = $iq({
|
||||
'to': Candy.Util.escapeJid(roomJid),
|
||||
'type': 'set'
|
||||
});
|
||||
|
||||
iq.c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
|
||||
.c('item', {'nick': nick, 'role': role});
|
||||
|
||||
conn.sendIQ(iq.tree());
|
||||
};
|
||||
|
||||
var applyTranslations = function applyTranslations() {
|
||||
var addModeratorActionLabel = {
|
||||
'en' : 'Grant moderator status',
|
||||
'de' : 'Moderator status geben'
|
||||
};
|
||||
var removeModeratorActionLabel = {
|
||||
'en' : 'Remove moderator status',
|
||||
'de' : 'Moderator status nehmen'
|
||||
};
|
||||
|
||||
$.each(addModeratorActionLabel, function(k, v) {
|
||||
if(Candy.View.Translation[k]) {
|
||||
Candy.View.Translation[k].addModeratorActionLabel = v;
|
||||
}
|
||||
});
|
||||
$.each(removeModeratorActionLabel, function(k, v) {
|
||||
if(Candy.View.Translation[k]) {
|
||||
Candy.View.Translation[k].removeModeratorActionLabel = v;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var isOwnerOrAdmin = function(user) {
|
||||
return ['owner', 'admin'].indexOf(user.getAffiliation()) !== -1;
|
||||
};
|
||||
var isModerator = function(user) {
|
||||
return user.getRole() === 'moderator';
|
||||
};
|
||||
|
||||
/** Function: init
|
||||
* Initializes the plugin by adding an event which modifies
|
||||
* the contextmenu links.
|
||||
*/
|
||||
self.init = function init() {
|
||||
applyTranslations();
|
||||
|
||||
$(Candy).bind('candy:view.roster.context-menu', function(e, args) {
|
||||
args.menulinks.addModerator = {
|
||||
requiredPermission: function(user, me) {
|
||||
return me.getNick() !== user.getNick() && isOwnerOrAdmin(me) && !isOwnerOrAdmin(user) && !isModerator(user);
|
||||
},
|
||||
'class' : 'add-moderator',
|
||||
'label' : $.i18n._('addModeratorActionLabel'),
|
||||
'callback' : function(e, roomJid, user) {
|
||||
modifyRole('moderator', roomJid, user);
|
||||
}
|
||||
};
|
||||
args.menulinks.removeModerator = {
|
||||
requiredPermission: function(user, me) {
|
||||
return me.getNick() !== user.getNick() && isOwnerOrAdmin(me) && !isOwnerOrAdmin(user) && isModerator(user);
|
||||
},
|
||||
'class' : 'remove-moderator',
|
||||
'label' : $.i18n._('removeModeratorActionLabel'),
|
||||
'callback' : function(e, roomJid, user) {
|
||||
modifyRole('participant', roomJid, user);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.ModifyRole || {}, Candy, jQuery));
|
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
@ -0,0 +1,31 @@
|
||||
# Name completion plugin
|
||||
This plugin will complete the names of users in the room when a specified key is pressed.
|
||||
|
||||
### Usage
|
||||
<script type="text/javascript" src="path_to_plugins/namecomplete/candy.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="path_to_plugins/namecomplete/candy.css" />
|
||||
|
||||
...
|
||||
|
||||
CandyShop.NameComplete.init();
|
||||
|
||||
### Configuration options
|
||||
`nameIdentifier` - String - The identifier to look for in a string. Defaults to `'@'`
|
||||
|
||||
`completeKeyCode` - Integer - The key code of the key to use. Defaults to `9` (tab)
|
||||
|
||||
### Example configurations
|
||||
|
||||
// complete the name when the user types +nick and hits the right arrow
|
||||
// +troymcc -> +troymccabe
|
||||
CandyShop.NameComplete.init({
|
||||
nameIdentifier: '+',
|
||||
completeKeyCode: '39'
|
||||
});
|
||||
|
||||
// complete the name when the user types -nick and hits the up arrow
|
||||
// +troymcc ^ +troymccabe
|
||||
CandyShop.NameComplete.init({
|
||||
nameIdentifier: '-',
|
||||
completeKeyCode: '38'
|
||||
});
|
@ -0,0 +1,7 @@
|
||||
#context-menu li.selected {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
#context-menu li.candy-namecomplete-option {
|
||||
padding: 3px 5px;
|
||||
}
|
@ -0,0 +1,260 @@
|
||||
/** File: candy.js
|
||||
* Candy - Chats are not dead yet.
|
||||
*
|
||||
* Authors:
|
||||
* - Troy McCabe <troy.mccabe@geeksquad.com>
|
||||
* - Ben Klang <bklang@mojolingo.com>
|
||||
*
|
||||
* Copyright:
|
||||
* (c) 2012 Geek Squad. All rights reserved.
|
||||
* (c) 2014 Power Home Remodeling Group. All rights reserved.
|
||||
*/
|
||||
|
||||
/* global document, Candy, jQuery */
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
/** Class: CandyShop.NameComplete
|
||||
* Allows for completion of a name in the roster
|
||||
*/
|
||||
CandyShop.NameComplete = (function(self, Candy, $) {
|
||||
/** Object: _options
|
||||
* Options:
|
||||
* (String) nameIdentifier - Prefix to append to a name to look for. '@' now looks for '@NICK', '' looks for 'NICK', etc. Defaults to '@'
|
||||
* (Integer) completeKeyCode - Which key to use to complete
|
||||
*/
|
||||
var _options = {
|
||||
nameIdentifier: '@',
|
||||
completeKeyCode: 9
|
||||
};
|
||||
|
||||
/** Array: _nicks
|
||||
* An array of nicks to complete from
|
||||
* Populated after 'candy:core.presence'
|
||||
*/
|
||||
var _nicks = [];
|
||||
|
||||
/** String: _selector
|
||||
* The selector for the visible message box
|
||||
*/
|
||||
var _selector = 'input[name="message"]:visible';
|
||||
|
||||
/** Boolean:_autocompleteStarted
|
||||
* Keeps track of whether we're in the middle of autocompleting a name
|
||||
*/
|
||||
var _autocompleteStarted = false;
|
||||
|
||||
/** Function: init
|
||||
* Initialize the NameComplete plugin
|
||||
* Show options for auto completion of names
|
||||
*
|
||||
* Parameters:
|
||||
* (Object) options - Options to apply to this plugin
|
||||
*/
|
||||
self.init = function(options) {
|
||||
// apply the supplied options to the defaults specified
|
||||
$.extend(true, _options, options);
|
||||
|
||||
// listen for keydown when autocomplete options exist
|
||||
$(document).on('keypress', _selector, function(e) {
|
||||
if (e.which === _options.nameIdentifier.charCodeAt()) {
|
||||
_autocompleteStarted = true;
|
||||
}
|
||||
|
||||
if (_autocompleteStarted) {
|
||||
// update the list of nicks to grab
|
||||
self.populateNicks();
|
||||
|
||||
// set up the vars for this method
|
||||
// break it on spaces, and get the last word in the string
|
||||
var field = $(this);
|
||||
var msgParts = field.val().split(' ');
|
||||
var lastWord = new RegExp( "^" + msgParts[msgParts.length - 1] + String.fromCharCode(e.which), "i");
|
||||
var matches = [];
|
||||
|
||||
// go through each of the nicks and compare it
|
||||
$(_nicks).each(function(index, item) {
|
||||
// if we have results
|
||||
if (item.match(lastWord) !== null) {
|
||||
matches.push(item);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// if we only have one match, no need to show the picker, just replace it
|
||||
// else show the picker of the name matches
|
||||
if (matches.length === 1) {
|
||||
self.replaceName(matches[0]);
|
||||
// Since the name will be autocompleted, throw away the last character
|
||||
e.preventDefault();
|
||||
} else if (matches.length > 1) {
|
||||
self.showPicker(matches, field);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** Function: keyDown
|
||||
* The listener for keydown in the menu
|
||||
*/
|
||||
self.keyDown = function(e) {
|
||||
// get the menu and the content element
|
||||
var menu = $('#context-menu');
|
||||
var content = menu.find('ul');
|
||||
var selected = content.find('li.selected');
|
||||
|
||||
if(menu.css('display') === 'none') {
|
||||
$(document).unbind('keydown', self.keyDown);
|
||||
return;
|
||||
}
|
||||
|
||||
// switch the key code
|
||||
switch (e.which) {
|
||||
// up arrow
|
||||
case 38:
|
||||
// down arrow
|
||||
case 40:
|
||||
var newEl;
|
||||
if (e.which === 38) {
|
||||
// move the selected thing up
|
||||
newEl = selected.prev();
|
||||
} else {
|
||||
// move the selected thing down
|
||||
newEl = selected.next();
|
||||
}
|
||||
// Prevent going off either end of the list
|
||||
if ($(newEl).length > 0) {
|
||||