Интерфейс на основе candy
|
@ -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.
|
||||
|
||||
![Color Picker](screenshot.png)
|
||||
|
||||
## 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));
|
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).
|
||||
|
||||
![Color Picker](screenshot.png)
|
||||
|
||||
## 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));
|
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.
|
||||
|
||||
![Left Tabs + Bootstrap3](screenshot.png)
|
||||
|
||||
## 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,104 @@
|
|||
/** File: lefttabs.js
|
||||
* Candy Plugin Left Tabs + Bootstrap3 Layout
|
||||
* Author: Melissa Adamaitis <madamei@mojolingo.com>
|
||||
*/
|
||||
|
||||
/* global window, Candy, jQuery */
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
CandyShop.LeftTabs = (function(self, Candy, $) {
|
||||
/**
|
||||
* Initializes the LeftTabs plugin with the default settings.
|
||||
*/
|
||||
self.init = function(){
|
||||
Candy.View.Template.Chat = {
|
||||
pane: '<div class="row" id="chat-pane">{{> tabs}}{{> toolbar}}{{> rooms}}</div>{{> modal}}',
|
||||
rooms: '<div id="chat-rooms" class="rooms"></div>',
|
||||
tabs: '<div id="left-menu-wrapper"><iframe width="100%" height="510" src="https://www.youtube.com/embed/yG0oBPtyNb0" frameborder="0" allowfullscreen autoplay="1"></iframe></div>',
|
||||
tab: '<li class="roomtype-{{roomType}}" data-roomjid="{{roomJid}}" data-roomtype="{{roomType}}">' +
|
||||
'<a href="#" class="label">{{#privateUserChat}}<span class="glyphicon glyphicon-user"></span> {{/privateUserChat}}{{name}}</a>' +
|
||||
'<a href="#" class="transition"></a><a href="#" class="close">\u00D7</a>' +
|
||||
'<small class="unread"></small></li>',
|
||||
modal: '<div id="chat-modal"><a id="admin-message-cancel" class="close" href="#">\u00D7</a>' +
|
||||
'<span id="chat-modal-body"></span>' +
|
||||
'<img src="{{resourcesPath}}img/modal-spinner.gif" id="chat-modal-spinner" />' +
|
||||
'</div><div id="chat-modal-overlay"></div>',
|
||||
adminMessage: '<li><small>{{time}}</small><div class="adminmessage">' +
|
||||
'<span class="label">{{sender}}</span>' +
|
||||
'<span class="spacer">▸</span>{{subject}} {{message}}</div></li>',
|
||||
infoMessage: '<li><small>{{time}}</small><div class="infomessage">' +
|
||||
'<span class="spacer">•</span>{{subject}} {{message}}</div></li>',
|
||||
toolbar: '<ul id="chat-toolbar">' +
|
||||
'<li id="emoticons-icon" data-tooltip="{{tooltipEmoticons}}"><span class="glyphicon glyphicon-asterisk"></span></li>' +
|
||||
'<li id="chat-sound-control" class="checked" data-tooltip="{{tooltipSound}}"><span class="glyphicon glyphicon-volume-up"></span><span class="glyphicon glyphicon-volume-off"></span>{{> soundcontrol}}</li>' +
|
||||
'<li id="chat-autoscroll-control" class="checked" data-tooltip="{{tooltipAutoscroll}}"><span class="glyphicon glyphicon-arrow-down"></span><span class="glyphicon glyphicon-ban-circle"></span></li>' +
|
||||
'<li class="checked" id="chat-statusmessage-control" data-tooltip="{{tooltipStatusmessage}}"><span class="glyphicon glyphicon-info-sign"></span><span class="glyphicon glyphicon-ban-circle"></span></li>' +
|
||||
'<li class="context" data-tooltip="{{tooltipAdministration}}"></li>' +
|
||||
'<li class="usercount" data-tooltip="{{tooltipUsercount}}"><span class="glyphicon glyphicon-user"></span>' +
|
||||
'<span id="chat-usercount"></span></li></ul>',
|
||||
soundcontrol: '<script type="text/javascript">var audioplayerListener = new Object();' +
|
||||
' audioplayerListener.onInit = function() { };' +
|
||||
'</script><object id="chat-sound-player" type="application/x-shockwave-flash" data="{{resourcesPath}}audioplayer.swf"' +
|
||||
' width="0" height="0"><param name="movie" value="{{resourcesPath}}audioplayer.swf" /><param name="AllowScriptAccess"' +
|
||||
' value="always" /><param name="FlashVars" value="listener=audioplayerListener&mp3={{resourcesPath}}notify.mp3" />' +
|
||||
'</object>',
|
||||
Context: {
|
||||
menu: '<div id="context-menu"><i class="arrow arrow-top"></i>' +
|
||||
'<ul></ul><i class="arrow arrow-bottom"></i></div>',
|
||||
menulinks: '<li class="{{class}}" id="context-menu-{{id}}">{{label}}</li>',
|
||||
contextModalForm: '<form action="#" id="context-modal-form">' +
|
||||
'<label for="context-modal-label">{{_label}}</label>' +
|
||||
'<input type="text" name="contextModalField" id="context-modal-field" />' +
|
||||
'<input type="submit" class="button" name="send" value="{{_submit}}" /></form>',
|
||||
adminMessageReason: '<a id="admin-message-cancel" class="close" href="#">×</a>' +
|
||||
'<p>{{_action}}</p>{{#reason}}<p>{{_reason}}</p>{{/reason}}'
|
||||
},
|
||||
tooltip: '<div id="tooltip"><i class="arrow arrow-top"></i>' +
|
||||
'<div></div><i class="arrow arrow-bottom"></i></div>'
|
||||
};
|
||||
|
||||
// Make the message pane the full height of the window.
|
||||
self.heights();
|
||||
|
||||
// Make sure that the window heights are the right size after the window is resized.
|
||||
$(window).resize(function() {
|
||||
self.heights();
|
||||
});
|
||||
|
||||
// Make sure that the window heights are the right size after a new room is added.
|
||||
$(Candy).on('candy:view.room.after-add', function() {
|
||||
self.heights();
|
||||
if(typeof CandyShop.CreateRoom === "object") {
|
||||
self.createRoomPluginCompatibility();
|
||||
}
|
||||
});
|
||||
|
||||
$(Candy).on('candy:view.message.after-show', function(ev, obj) {
|
||||
if(Candy.View.Pane.Window.autoscroll) {
|
||||
$('div[data-roomjid="' + obj.roomJid + '"] .message-pane').scrollTop($('div[data-roomjid="' + obj.roomJid + '"] .message-pane').prop('scrollHeight') + $('div[data-roomjid="' + obj.roomJid + '"] .message-form-wrapper').height());
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
self.heights = function() {
|
||||
var barless_height = $(window).height() - $('.message-form-wrapper').height();
|
||||
var message_pane_height = barless_height;
|
||||
var message_pane_wrapper_height = (barless_height - parseInt($('.message-pane-wrapper').css('padding-bottom')));
|
||||
if(CandyShop.RoomBar) {
|
||||
message_pane_height = barless_height - parseInt($('.roombar').css('height'));
|
||||
$('.message-pane').css('margin-top', parseInt($('.roombar').css('height')) + 'px');
|
||||
}
|
||||
$('.message-pane-wrapper').height(message_pane_wrapper_height + 'px');
|
||||
$('.message-pane').height(message_pane_height + 'px');
|
||||
$('.roster-pane').height(barless_height + 'px');
|
||||
};
|
||||
|
||||
self.createRoomPluginCompatibility = function() {
|
||||
$('#create-group-form button').addClass('btn');
|
||||
$('#create-group-form .close-button').html('<span class="glyphicon glyphicon-remove"></span>');
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.LeftTabs || {}, Candy, jQuery));
|
|
@ -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
|
||||
```
|
||||
|
||||
![Color Picker](me-does-screenshot.png)
|
||||
|
||||
**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.
|
||||
|
||||
![Modify role screenshot](screenshot.png)
|
||||
|
||||
## 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
|
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));
|
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) {
|
||||
selected.removeClass('selected');
|
||||
newEl.addClass('selected');
|
||||
}
|
||||
// don't perform any key actions
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
// esc key
|
||||
case 27:
|
||||
// delete Key
|
||||
case 8:
|
||||
case 46:
|
||||
self.endAutocomplete();
|
||||
break;
|
||||
|
||||
// the key code for completion
|
||||
case _options.completeKeyCode:
|
||||
case 13:
|
||||
// get the text of the selected item
|
||||
var val = content.find('li.selected').text();
|
||||
// replace the last item with the selected item
|
||||
self.replaceName(val);
|
||||
// don't perform any key actions
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/** Function: endAutocomplete
|
||||
* Disables autocomplete mode, hiding the context menu
|
||||
*/
|
||||
self.endAutocomplete = function() {
|
||||
_autocompleteStarted = false;
|
||||
$(_selector).unbind('keydown', self.keyDown);
|
||||
$('#context-menu').hide();
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Function: selectOnClick
|
||||
* The listener for click on decision in the menu
|
||||
*
|
||||
* Parameters:
|
||||
* (Event) e - The click event
|
||||
*/
|
||||
self.selectOnClick = function(e) {
|
||||
self.replaceName($(e.currentTarget).text());
|
||||
$(_selector).focus();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
/** Function: populateNicks
|
||||
* Populate the collection of nicks to autocomplete from
|
||||
*/
|
||||
self.populateNicks = function() {
|
||||
// clear the nick collection
|
||||
_nicks = [];
|
||||
|
||||
// grab the roster in the current room
|
||||
var room = Candy.Core.getRoom(Candy.View.getCurrent().roomJid);
|
||||
if (room !== null) {
|
||||
var roster = room.getRoster().getAll();
|
||||
|
||||
// iterate and add the nicks to the collection
|
||||
$.each(roster, function(index, item) {
|
||||
_nicks.push(_options.nameIdentifier + item.getNick());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** Function: replaceName
|
||||
*
|
||||
*/
|
||||
self.replaceName = function(replaceText) {
|
||||
// get the parts of the message
|
||||
var $msgBox = $(_selector);
|
||||
var msgParts = $msgBox.val().split(' ');
|
||||
|
||||
// If the name is the first word, add a colon to the end
|
||||
if (msgParts.length === 1) {
|
||||
replaceText += ": ";
|
||||
} else {
|
||||
replaceText += " ";
|
||||
}
|
||||
|
||||
// replace the last part with the item
|
||||
msgParts[msgParts.length - 1] = replaceText;
|
||||
|
||||
// put the string back together on spaces
|
||||
$msgBox.val(msgParts.join(' '));
|
||||
self.endAutocomplete();
|
||||
};
|
||||
|
||||
/** Function: showPicker
|
||||
* Show the picker for the list of names that match
|
||||
*/
|
||||
self.showPicker = function(matches, elem) {
|
||||
// get the element
|
||||
elem = $(elem);
|
||||
|
||||
// get the necessary items
|
||||
var pos = elem.offset(),
|
||||
menu = $('#context-menu'),
|
||||
content = $('ul', menu),
|
||||
i;
|
||||
|
||||
// clear the content if needed
|
||||
content.empty();
|
||||
|
||||
// add the matches to the list
|
||||
for(i = 0; i < matches.length; i++) {
|
||||
content.append('<li class="candy-namecomplete-option">' + matches[i] + '</li>');
|
||||
}
|
||||
|
||||
// select the first item
|
||||
$(content.find('li')[0]).addClass('selected');
|
||||
|
||||
content.find('li').click(self.selectOnClick);
|
||||
|
||||
// bind the keydown to move around the menu
|
||||
$(_selector).bind('keydown', self.keyDown);
|
||||
|
||||
var posLeft = elem.val().length * 7,
|
||||
posTop = Candy.Util.getPosTopAccordingToWindowBounds(menu, pos.top);
|
||||
|
||||
// show it
|
||||
menu.css({'left': posLeft, 'top': posTop.px, backgroundPosition: posLeft.backgroundPositionAlignment + ' ' + posTop.backgroundPositionAlignment});
|
||||
menu.fadeIn('fast');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.NameComplete || {}, Candy, jQuery));
|
|
@ -0,0 +1,26 @@
|
|||
# Nickchange
|
||||
Enable your users to change the nick using a toolbar icon.
|
||||
|
||||
![Nickchange Icon](screenshot.png)
|
||||
|
||||
## Usage
|
||||
To enable *Nickchange* you have to include its JavaScript code and stylesheet:
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/nickchange/candy.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="candyshop/nickchange/candy.css" />
|
||||
```
|
||||
|
||||
Call its `init()` method after Candy has been initialized:
|
||||
|
||||
```JavaScript
|
||||
Candy.init('/http-bind/');
|
||||
|
||||
// enable Nickchange plugin
|
||||
CandyShop.Nickchange.init();
|
||||
|
||||
Candy.Core.connect();
|
||||
```
|
||||
|
||||
## Credits
|
||||
Thanks to [famfamfam silk icons](http://www.famfamfam.com/lab/icons/silk/) for the rename icon.
|
|
@ -0,0 +1,3 @@
|
|||
#nickchange-control {
|
||||
background: no-repeat url('nickchange-control.png');
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* Nickchange plugin for Candy
|
||||
*
|
||||
* Copyright 2014 Michael Weibel <michael.weibel@gmail.com>
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
/* global Candy, jQuery, Mustache */
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
CandyShop.Nickchange = (function(self, Candy, $) {
|
||||
|
||||
self.init = function() {
|
||||
self.applyTranslations();
|
||||
|
||||
var html = '<li id="nickchange-control" data-tooltip="' + $.i18n._('candyshopNickchange') + '"></li>';
|
||||
$('#emoticons-icon').after(html);
|
||||
$('#nickchange-control').click(function() {
|
||||
self.showModal();
|
||||
});
|
||||
};
|
||||
|
||||
self.showModal = function() {
|
||||
Candy.View.Pane.Chat.Modal.show(Mustache.to_html(self.nicknameChangeForm, {
|
||||
_labelNickname: $.i18n._('labelNickname'),
|
||||
_label: $.i18n._('candyshopNickchange')
|
||||
}));
|
||||
$('#nickname').focus();
|
||||
|
||||
// register submit handler
|
||||
$('#nickname-change-form').submit(self.changeNickname);
|
||||
};
|
||||
|
||||
self.changeNickname = function() {
|
||||
var nickname = $('#nickname').val();
|
||||
Candy.View.Pane.Chat.Modal.hide(function() {
|
||||
Candy.Core.Action.Jabber.SetNickname(nickname);
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
self.nicknameChangeForm = '<strong>{{_label}}</strong>' +
|
||||
'<form method="post" id="nickname-change-form" class="nickname-change-form">' +
|
||||
'<label for="nickname">{{_labelNickname}}</label><input type="text" id="nickname" name="nickname" />' +
|
||||
'<input type="submit" class="button" value="{{_label}}" /></form>';
|
||||
|
||||
self.applyTranslations = function() {
|
||||
var translations = {
|
||||
'en' : 'Change nickname',
|
||||
'de' : 'Spitzname ändern'
|
||||
};
|
||||
$.each(translations, function(k, v) {
|
||||
if(Candy.View.Translation[k]) {
|
||||
Candy.View.Translation[k].candyshopNickchange = v;
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.Nickchange || {}, Candy, jQuery));
|
After Width: | Height: | Size: 273 B |
|
@ -0,0 +1,29 @@
|
|||
# Notifications
|
||||
Send HTML5 Notifications when a message is received and the window is not in focus. This only works with webkit browsers.
|
||||
|
||||
## Usage
|
||||
To enable *Notifications* you have to include its JavaScript code and stylesheet:
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/notifications/candy.js"></script>
|
||||
```
|
||||
|
||||
Call its `init()` method after Candy has been initialized:
|
||||
|
||||
```JavaScript
|
||||
Candy.init('/http-bind/');
|
||||
|
||||
CandyShop.Notifications.init();
|
||||
|
||||
Candy.Core.connect();
|
||||
```
|
||||
|
||||
It is possible to configure the Plugin.
|
||||
|
||||
```JavaScript
|
||||
CandyShop.Notifications.init({
|
||||
notifyNormalMessage: false, // Send a notification for every message. Defaults to false
|
||||
notifyPersonalMessage: true, // Send a notification if the user is mentioned. (Requires NotfiyMe Plugin) Defaults to true
|
||||
closeTime: 3000 // Close notification after X milliseconds. Zero means it doesn't close automaticly. Defaults to 3000
|
||||
});
|
||||
```
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* HTML5 Notifications
|
||||
* @version 1.0
|
||||
* @author Jonatan Männchen <jonatan@maennchen.ch>
|
||||
* @author Melissa Adamaitis <madamei@mojolingo.com>
|
||||
*
|
||||
* Notify user if new messages come in.
|
||||
*/
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
CandyShop.Notifications = (function(self, Candy, $) {
|
||||
/** Object: _options
|
||||
* Options for this plugin's operation
|
||||
*
|
||||
* Options:
|
||||
* (Boolean) notifyNormalMessage - Notification on normalmessage. Defaults to false
|
||||
* (Boolean) notifyPersonalMessage - Notification for private messages. Defaults to true
|
||||
* (Boolean) notifyMention - Notification for mentions. Defaults to true
|
||||
* (Integer) closeTime - Time until closing the Notification. (0 = Don't close) Defaults to 3000
|
||||
* (String) title - Title to be used in notification popup. Set to null to use the contact's name.
|
||||
* (String) icon - Path to use for image/icon for notification popup.
|
||||
*/
|
||||
var _options = {
|
||||
notifyNormalMessage: false,
|
||||
notifyPersonalMessage: true,
|
||||
notifyMention: true,
|
||||
closeTime: 3000,
|
||||
title: null,
|
||||
icon: window.location.origin + '/' + Candy.View.getOptions().assets + '/img/favicon.png'
|
||||
};
|
||||
|
||||
/** Function: init
|
||||
* Initializes the notifications plugin.
|
||||
*
|
||||
* Parameters:
|
||||
* (Object) options - The options to apply to this plugin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
self.init = function(options) {
|
||||
// apply the supplied options to the defaults specified
|
||||
$.extend(true, _options, options);
|
||||
|
||||
// Just init if notifications are supported
|
||||
if (window.Notification) {
|
||||
// Setup Permissions (has to be kicked on with some user-events)
|
||||
jQuery(document).one('click keydown', self.setupPermissions);
|
||||
|
||||
// Add Listener for Notifications
|
||||
$(Candy).on('candy:view.message.notify', self.handleNotification);
|
||||
}
|
||||
};
|
||||
|
||||
/** Function: checkPermissions
|
||||
* Check if the plugin has permission to send notifications.
|
||||
*
|
||||
* @return boid
|
||||
*/
|
||||
self.setupPermissions = function() {
|
||||
// Check if permissions is given
|
||||
if (window.Notification !== 0) { // 0 is PERMISSION_ALLOWED
|
||||
// Request for it
|
||||
window.Notification.requestPermission();
|
||||
}
|
||||
};
|
||||
|
||||
/** Function: handleNotification
|
||||
* Descriptions
|
||||
*
|
||||
* Parameters:
|
||||
* (Array) args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
self.handleNotification = function(e, args) {
|
||||
// Check if window has focus, so no notification needed
|
||||
if (!document.hasFocus()) {
|
||||
if(_options.notifyNormalMessage ||
|
||||
(self.mentionsMe(args.message) && _options.notifyMention) ||
|
||||
(_options.notifyPersonalMessage && Candy.View.Pane.Chat.rooms[args.roomJid].type === 'chat')) {
|
||||
// Create the notification.
|
||||
var title = !_options.title ? args.name : _options.title ,
|
||||
notification = new window.Notification(title, {
|
||||
icon: _options.icon,
|
||||
body: args.message
|
||||
});
|
||||
|
||||
// Close it after 3 Seconds
|
||||
if(_options.closeTime) {
|
||||
window.setTimeout(function() { notification.close(); }, _options.closeTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.mentionsMe = function(message) {
|
||||
var message = message.toLowerCase(),
|
||||
nick = Candy.Core.getUser().getNick().toLowerCase(),
|
||||
cid = Strophe.getNodeFromJid(Candy.Core.getUser().getJid()).toLowerCase(),
|
||||
jid = Candy.Core.getUser().getJid().toLowerCase();
|
||||
if (message.indexOf(nick) === -1 &&
|
||||
message.indexOf(cid) === -1 &&
|
||||
message.indexOf(jid) === -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return self;
|
||||
}(CandyShop.Notifications || {}, Candy, jQuery));
|
|
@ -0,0 +1,29 @@
|
|||
# Reply Highlighting
|
||||
|
||||
To better support conversations in high-activity rooms, this plugin highlights any message that contains "@yourusername" by default.
|
||||
|
||||
## Usage
|
||||
|
||||
```HTML
|
||||
<script type="text/javascript" src="candyshop/replies/candy.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="candyshop/replies/candy.css" />
|
||||
```
|
||||
|
||||
```JavaScript
|
||||
CandyShop.Replies.init();
|
||||
```
|
||||
|
||||
|
||||
```Options
|
||||
boolean - default true - require @ if true
|
||||
prefix - strip a prefix while searching
|
||||
suffix - strip a suffix while searching
|
||||
```
|
||||
|
||||
Prefix & suffix assume generated user names for an anonymous user. For example, say your generated nick is _user533_ , and they change their nickname to _jimbob_. With the options:
|
||||
|
||||
```JavaScript
|
||||
CandyShop.Replies.init(false,'user','');
|
||||
```
|
||||
|
||||
This would highlight lines with _user533_, _533_, and _jimbob_ in them.
|
|
@ -0,0 +1,4 @@
|
|||
.message-pane li.mention,
|
||||
.message-pane li.mention small {
|
||||
background-color: #FFF7DE;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* candy-replies-plugin
|
||||
* @version 0.4 (2015-02-05)
|
||||
* @author Drew Harry (drew.harry@gmail.com)
|
||||
* Contributors:
|
||||
* - Sudrien <_+github@sudrien.net>
|
||||
*
|
||||
* Adds @reply highlighting to chat messages to help with high velocity
|
||||
* conversations.
|
||||
*/
|
||||
|
||||
/* global Candy, jQuery */
|
||||
|
||||
var CandyShop = (function(self) { return self; }(CandyShop || {}));
|
||||
|
||||
CandyShop.Replies = (function(self, Candy, $) {
|
||||
|
||||
var requireAt = true,
|
||||
prefix = '',
|
||||
suffix = '';
|
||||
|
||||
self.init = function( requireAtValue, prefixValue, suffixValue ) {
|
||||
requireAt = typeof requireAtValue !== 'undefined' ? requireAtValue : true;
|
||||
prefix = prefixValue !== undefined ? prefixValue : '';
|
||||
suffix = suffixValue !== undefined ? suffixValue : '';
|
||||
|
||||
$(Candy).on('candy:view.message.after-show', handleOnShow);
|
||||
return self;
|
||||
};
|
||||
|
||||
var handleOnShow = function(e, args) {
|
||||
var possibleNicks = $('.me').map(function(){ return $(this).attr('data-nick'); });
|
||||
possibleNicks.push(Candy.Core.getUser().getNick());
|
||||
|
||||
$.unique(possibleNicks).each(function(key,nick) {
|
||||
if( RegExp("(\\W|^)" + ( requireAt ? '@' : '' ) + nick + "(\\W|$)" , "im").test(args.message) ) {
|
||||
$(args.element).addClass("mention");
|
||||
}
|
||||
if( prefix !== '' || suffix !== '') {
|
||||
var shortNick = nick.replace( RegExp("^" + prefix), "").replace( RegExp( suffix + "$"), "");
|
||||
if( RegExp("(\\W|^)" + ( requireAt ? '@' : '' ) + shortNick + "(\\W|$)" , "im").test(args.message) ) {
|
||||
$(args.element).addClass("mention");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
}(CandyShop.Replies || {}, Candy, jQuery));
|
|
@ -0,0 +1,69 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>IF XMPP chat</title>
|
||||
<link rel="shortcut icon" href="res/img/favicon.png" type="image/gif" />
|
||||
<link rel="stylesheet" type="text/css" href="res/default.css" />
|
||||
|
||||
<!-- Plugins -->
|
||||
<link rel="stylesheet" type="text/css" href="candy-plugins/colors-xhtml/candy.css" />
|
||||
<link rel="stylesheet" type="text/css" href="candy-plugins/inline-images/candy.css" />
|
||||
<link rel="stylesheet" type="text/css" href="candy-plugins/modify-role/candy.css" />
|
||||
<link rel="stylesheet" type="text/css" href="candy-plugins/namecomplete/candy.css" />
|
||||
<link rel="stylesheet" type="text/css" href="candy-plugins/nickchange/candy.css" />
|
||||
<link rel="stylesheet" type="text/css" href="candy-plugins/replies/candy.css" />
|
||||
<link rel="stylesheet" type="text/css" href="candy-plugins/lefttabs/lefttabs.css" />
|
||||
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="libs.min.js"></script>
|
||||
<script type="text/javascript" src="candy.min.js"></script>
|
||||
<!-- Plugins -->
|
||||
<script type="text/javascript" src="candy-plugins/colors-xhtml/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/chatrecall/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/emphasis/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/me-does/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/modify-role/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/namecomplete/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/nickchange/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/notifications/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/replies/candy.js"></script>
|
||||
<script type="text/javascript" src="candy-plugins/lefttabs/lefttabs.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
CandyShop.LeftTabs.init();
|
||||
Candy.init('https://oreolek.ru:5281/http-bind/', {
|
||||
core: {
|
||||
debug: false,
|
||||
autojoin: ['webinar@conference.oreolek.ru'],
|
||||
},
|
||||
view: {
|
||||
assets: 'res/',
|
||||
language: 'ru',
|
||||
// enable XHTML for colors plugin
|
||||
enableXHTML: true
|
||||
}
|
||||
});
|
||||
|
||||
// enable Colors-XHTML plugin (default: 8 colors)
|
||||
CandyShop.ColorsXhtml.init();
|
||||
CandyShop.ChatRecall.init();
|
||||
// enable basic textile/BBCode/Html handling
|
||||
CandyShop.Emphasis.init();
|
||||
CandyShop.NameComplete.init();
|
||||
CandyShop.Replies.init();
|
||||
// enable /me handling
|
||||
CandyShop.MeDoes.init();
|
||||
CandyShop.ModifyRole.init();
|
||||
CandyShop.Nickchange.init();
|
||||
CandyShop.Notifications.init();
|
||||
|
||||
Candy.Core.connect("guest.oreolek.ru");
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="candy"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,674 @@
|
|||
/**
|
||||
* Chat CSS
|
||||
*
|
||||
* @author Michael <michael.weibel@gmail.com>
|
||||
* @author Patrick <patrick.stadler@gmail.com>
|
||||
|
||||
* @copyright 2011 Amiado Group AG, All rights reserved.
|
||||
* @copyright 2012-2014 Patrick Stadler & Michael Weibel. All rights reserved.
|
||||
*/
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Helvetica Neue', Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
#candy {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background-color: #444;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#chat-tabs {
|
||||
list-style: none;
|
||||
margin: 0 200px 0 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#chat-tabs li {
|
||||
margin: 0;
|
||||
float: left;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
margin: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
#chat-tabs a {
|
||||
padding: 4px 50px 4px 10px;
|
||||
display: inline-block;
|
||||
color: #ccc;
|
||||
height: 20px;
|
||||
background-color: #666;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
#chat-tabs .active a {
|
||||
background-color: #eee;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#chat-tabs .transition {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 0;
|
||||
width: 35px;
|
||||
height: 30px;
|
||||
background: url(img/tab-transitions.png) repeat-y left;
|
||||
border-radius: 0 3px 0 0;
|
||||
}
|
||||
|
||||
#chat-tabs a.close {
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
right: -2px;
|
||||
top: -3px;
|
||||
height: auto;
|
||||
padding: 5px;
|
||||
margin: 0 5px 0 2px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#chat-tabs .active .transition {
|
||||
background: url(img/tab-transitions.png) repeat-y -50px;
|
||||
}
|
||||
|
||||
#chat-tabs .close:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#chat-tabs .unread {
|
||||
color: white;
|
||||
background-color: #9b1414;
|
||||
padding: 2px 4px;
|
||||
font-weight: bold;
|
||||
font-size: 10px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 22px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#chat-tabs .offline .label {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
#chat-toolbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
width: 200px;
|
||||
height: 24px;
|
||||
padding-top: 7px;
|
||||
background-color: #444;
|
||||
display: none;
|
||||
border-top: 1px solid black;
|
||||
box-shadow: 0 1px 0 0 #555 inset;
|
||||
}
|
||||
|
||||
#chat-toolbar li {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 5px;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
background-position: top left;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#chat-toolbar #emoticons-icon {
|
||||
background-image: url(img/action/emoticons.png);
|
||||
}
|
||||
|
||||
#chat-toolbar .context {
|
||||
background-image: url(img/action/settings.png);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.role-moderator #chat-toolbar .context, .affiliation-owner #chat-toolbar .context {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#chat-sound-control {
|
||||
background-image: url(img/action/sound-off.png);
|
||||
}
|
||||
|
||||
#chat-sound-control.checked {
|
||||
background-image: url(img/action/sound-on.png);
|
||||
}
|
||||
|
||||
#chat-autoscroll-control {
|
||||
background-image: url(img/action/autoscroll-off.png);
|
||||
}
|
||||
|
||||
#chat-autoscroll-control.checked {
|
||||
background-image: url(img/action/autoscroll-on.png);
|
||||
}
|
||||
|
||||
#chat-statusmessage-control {
|
||||
background: url(img/action/statusmessage-off.png);
|
||||
}
|
||||
|
||||
#chat-statusmessage-control.checked {
|
||||
background: url(img/action/statusmessage-on.png);
|
||||
}
|
||||
|
||||
#chat-toolbar .usercount {
|
||||
background-image: url(img/action/usercount.png);
|
||||
cursor: default;
|
||||
padding-left: 20px;
|
||||
width: auto;
|
||||
margin-right: 5px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.usercount span {
|
||||
display: inline-block;
|
||||
padding: 1px 3px;
|
||||
background-color: #666;
|
||||
font-weight: bold;
|
||||
border-radius: 3px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.room-pane {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.roster-pane {
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
margin: 30px 0 32px 0;
|
||||
background-color: #333;
|
||||
border-top: 1px solid black;
|
||||
box-shadow: inset 0 1px 0 0 #555;
|
||||
}
|
||||
|
||||
.roster-pane .user {
|
||||
cursor: pointer;
|
||||
padding: 7px 10px;
|
||||
font-size: 12px;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
color: #ccc;
|
||||
clear: both;
|
||||
height: 14px;
|
||||
border-bottom: 1px solid black;
|
||||
box-shadow: 0 1px 0 0 #555;
|
||||
}
|
||||
|
||||
.roster-pane .user:hover {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.roster-pane .user.status-ignored {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.roster-pane .user.me {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.roster-pane .user.me:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.roster-pane .label {
|
||||
float: left;
|
||||
width: 110px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-shadow: 1px 1px black;
|
||||
}
|
||||
|
||||
.roster-pane li {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
float: right;
|
||||
display: block;
|
||||
margin-left: 3px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.roster-pane li.role {
|
||||
cursor: default;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.roster-pane li.role-moderator {
|
||||
background-image: url(img/roster/role-moderator.png);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.roster-pane li.affiliation-owner {
|
||||
background-image: url(img/roster/affiliation-owner.png);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.roster-pane li.ignore {
|
||||
background-image: url(img/roster/ignore.png);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.roster-pane .status-ignored li.ignore {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.roster-pane li.context {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.roster-pane li.context:hover {
|
||||
background-color: #666;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.roster-pane .me li.context {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message-pane-wrapper {
|
||||
clear: both;
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: auto;
|
||||
width: auto;
|
||||
margin: 30px 200px 31px 0;
|
||||
background-color: #eee;
|
||||
font-size: 13px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.message-pane {
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.message-pane li {
|
||||
cursor: default;
|
||||
border-bottom: 1px solid #ccc;
|
||||
box-shadow: 0 1px 0 0 white;
|
||||
}
|
||||
|
||||
.message-pane small {
|
||||
display: none;
|
||||
color: #a00;
|
||||
font-size: 10px;
|
||||
position: absolute;
|
||||
background-color: #f7f7f7;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
margin: 4px 0;
|
||||
padding: 0 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.message-pane li:hover {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.message-pane li:hover small {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.message-pane li>div {
|
||||
overflow: auto;
|
||||
padding: 2px 0 2px 130px;
|
||||
line-height: 24px;
|
||||
white-space: -o-pre-wrap; /* Opera */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
.message-pane li>div p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.message-pane .label {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
margin-left: -130px;
|
||||
width: 110px;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.message-pane .spacer {
|
||||
color: #aaa;
|
||||
font-weight: bold;
|
||||
margin-left: -14px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.message-pane .subject, .message-pane .subject .label {
|
||||
color: #a00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.message-pane .adminmessage {
|
||||
color: #a00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.message-pane .infomessage {
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.message-pane div>a {
|
||||
color: #a00;
|
||||
}
|
||||
|
||||
.message-pane a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.message-pane .emoticon {
|
||||
vertical-align: text-bottom;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.message-form-wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: auto;
|
||||
margin-right: 200px;
|
||||
border-top: 1px solid #ccc;
|
||||
background-color: white;
|
||||
height: 31px;
|
||||
}
|
||||
|
||||
.message-form {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-right: 320px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.message-form input {
|
||||
border: 0 none;
|
||||
padding: 5px 10px;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
outline-width: 0;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.message-form input.submit {
|
||||
cursor: pointer;
|
||||
background-color: #ccc;
|
||||
color: #666;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: 3px 203px 3px 3px;
|
||||
padding: 5px 7px;
|
||||
width: auto;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
height: 25px;
|
||||
font-weight: bold;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#tooltip {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
display: none;
|
||||
margin: 13px -18px -3px -2px;
|
||||
color: #333;
|
||||
font-size: 11px;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
#tooltip div {
|
||||
background-color: #f7f7f7;
|
||||
padding: 2px 5px;
|
||||
zoom: 1;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .75);
|
||||
}
|
||||
|
||||
.arrow {
|
||||
background: url(img/tooltip-arrows.gif) no-repeat left bottom;
|
||||
height: 5px;
|
||||
display: block;
|
||||
position: relative;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.right-bottom .arrow-bottom {
|
||||
background-position: right bottom;
|
||||
}
|
||||
|
||||
.arrow-top {
|
||||
display: none;
|
||||
background-position: left top;
|
||||
}
|
||||
|
||||
.right-top .arrow-top {
|
||||
display: block;
|
||||
background-position: right top;
|
||||
}
|
||||
|
||||
.left-top .arrow-top {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.left-top .arrow-bottom,
|
||||
.right-top .arrow-bottom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#context-menu {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
display: none;
|
||||
padding: 5px 10px;
|
||||
margin: 13px -28px -3px -12px;
|
||||
}
|
||||
|
||||
#context-menu ul {
|
||||
background-color: #f7f7f7;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
padding: 2px;
|
||||
zoom: 1;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .75);
|
||||
}
|
||||
|
||||
#context-menu li {
|
||||
padding: 3px 5px 3px 20px;
|
||||
line-height: 12px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2px;
|
||||
background: 1px no-repeat;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#context-menu li:hover {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
#context-menu li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#context-menu .private {
|
||||
background-image: url(img/action/private.png);
|
||||
}
|
||||
|
||||
#context-menu .ignore {
|
||||
background-image: url(img/action/ignore.png);
|
||||
}
|
||||
|
||||
#context-menu .unignore {
|
||||
background-image: url(img/action/unignore.png);
|
||||
}
|
||||
|
||||
#context-menu .kick {
|
||||
background-image: url(img/action/kick.png);
|
||||
}
|
||||
|
||||
#context-menu .ban {
|
||||
background-image: url(img/action/ban.png);
|
||||
}
|
||||
|
||||
#context-menu .subject {
|
||||
background-image: url(img/action/subject.png);
|
||||
}
|
||||
|
||||
#context-menu .emoticons {
|
||||
padding-left: 5px;
|
||||
width: 85px;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#context-menu .emoticons:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#context-menu .emoticons img {
|
||||
cursor: pointer;
|
||||
margin: 3px;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
#chat-modal {
|
||||
background: #eee;
|
||||
width: 300px;
|
||||
padding: 20px 5px;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -160px;
|
||||
margin-top: -45px;
|
||||
text-align: center;
|
||||
display: none;
|
||||
z-index: 100;
|
||||
border: 5px solid #888;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 5px black;
|
||||
}
|
||||
|
||||
#chat-modal-overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 90;
|
||||
background-image: url(img/overlay.png);
|
||||
}
|
||||
|
||||
#chat-modal.modal-login {
|
||||
display: block;
|
||||
margin-top: -100px;
|
||||
}
|
||||
|
||||
#chat-modal-spinner {
|
||||
display: none;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#chat-modal form {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
#chat-modal label, #chat-modal input, #chat-modal select {
|
||||
display: block;
|
||||
float: left;
|
||||
line-height: 26px;
|
||||
font-size: 16px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#chat-modal input, #chat-modal select {
|
||||
padding: 2px;
|
||||
line-height: 16px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#chat-modal input[type='text'],
|
||||
#chat-modal input[type='password'] {
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
padding: 4px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#chat-modal label {
|
||||
text-align: right;
|
||||
padding-right: 1em;
|
||||
clear: both;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
#chat-modal input.button {
|
||||
float: none;
|
||||
display: block;
|
||||
margin: 5px auto;
|
||||
clear: both;
|
||||
position: relative;
|
||||
top: 10px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#chat-modal .close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: none;
|
||||
padding: 0 5px;
|
||||
margin: -17px 3px 0 0;
|
||||
color: #999;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#chat-modal .close:hover {
|
||||
color: #333;
|
||||
background-color: #aaa;
|
||||
}
|
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 223 B |
After Width: | Height: | Size: 796 B |
After Width: | Height: | Size: 725 B |
After Width: | Height: | Size: 715 B |
After Width: | Height: | Size: 859 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 557 B |
After Width: | Height: | Size: 744 B |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 544 B |
After Width: | Height: | Size: 764 B |
After Width: | Height: | Size: 659 B |
After Width: | Height: | Size: 413 B |
After Width: | Height: | Size: 781 B |
After Width: | Height: | Size: 753 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 775 B |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 751 B |
|
@ -0,0 +1,2 @@
|
|||
Simple Smileys is a set of 49 clean, free as in freedom, Public Domain smileys.
|
||||
For more packages or older versions, visit http://simplesmileys.org
|
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 572 B |
After Width: | Height: | Size: 530 B |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 704 B |