Commit d8285372 authored by 神楽坂玲奈's avatar 神楽坂玲奈

candy-plugins

parent bb7407a5
...@@ -3,13 +3,32 @@ ...@@ -3,13 +3,32 @@
This is the official plugin repository for [Candy](http://candy-chat.github.com/candy), a JavaScript based multi-user chat client. This is the official plugin repository for [Candy](http://candy-chat.github.com/candy), a JavaScript based multi-user chat client.
## List of available plugins ## List of available plugins
* __available-rooms__ A plugin to show & join public rooms.
* __Chat Recall__ - Saves the last {x} messages to scroll through with up and down arrows, similar to terminal/cmd.
* __Clearchat__ - Clears chat window on click or if typing `/clear`
* __Colors__ - Send and receive colored messages. * __Colors__ - Send and receive colored messages.
* __Inline Images__ - If a user posts a URL to an image, that image gets rendered directly inside of Candy. * __Inline Images__ - If a user posts a URL to an image, that image gets rendered directly inside of Candy.
* __Inline Videos__ - If a user posts a URL to youtube video, it embeds the youtube video iframe into Candy.
* __join__ A plugin that allows to type `/join room [password]` to join a room.
* __jQuery-Ui__ - jQuery UI lightness theme * __jQuery-Ui__ - jQuery UI lightness theme
* __Namecomplete__ - Provides auto-complete of user names that are currently in the chat room.
* __Nickchange__ - Enable your users to change the nick using a toolbar icon
* __Notifications__ - OS Notifications in webkit
* __Notify Me__ - Notify me either through highlighting or audio that my username was mentioned.
* __Refocus__ - This plugin puts the focus on the entry box if the user clicks somewhere in the message list.
* __Remove Ignore__ - Removes the option to ignore/unignore a user from the roster.
* __Replies__ - Highlight any message that contains "@my_username"
* __Room Panel__ - Provides a list of rooms available to join. * __Room Panel__ - Provides a list of rooms available to join.
* __Sticky Subject__ - Retains the subject of the room underneath the tab itself.
* __Timeago__ - Replaces the exact time/date with fuzzy timestamps like "2 minutes ago". * __Timeago__ - Replaces the exact time/date with fuzzy timestamps like "2 minutes ago".
Support & Community ## Contributing
------------------- Please submit a pull request with your plugin or your changes to a plugin. We'll gladly merge it.
After a successful merge of a pull request, we will give you **push access** to this repository. You can then update your plugin on your own. If you update other plugins, please consider creating a pull request in order to inform the original plugin owner.
When contributing, please make sure that your code is of **high quality** and similar to other code in this repository. Also please submit a **screenshot** and a **README.md**.
## Support & Community
Take a look at our [FAQ](https://github.com/candy-chat/candy/wiki/Frequently-Asked-Questions). If it doesn't solve your questions, you're welcome to join our [Mailinglist on Google Groups](http://groups.google.com/group/candy-chat). Take a look at our [FAQ](https://github.com/candy-chat/candy/wiki/Frequently-Asked-Questions). If it doesn't solve your questions, you're welcome to join our [Mailinglist on Google Groups](http://groups.google.com/group/candy-chat).
You don't need to have a Gmail account for it. You don't need to have a Gmail account for it.
# Available Rooms Plugin
A plugin to join public rooms.
## Usage
Include the JavaScript and CSS files:
```HTML
<script type="text/javascript" src="candyshop/available-rooms/candy.js"></script>
```
To enable the Paint Plugin, just add one of the ´init´ methods to your bootstrap:
```JavaScript
CandyShop.AvailableRooms.init();
```
## Screenshot
![Screenshot](https://github.com/amiadogroup/candy-plugins/raw/master/available-rooms/screenshot.png)
/** File: candy.js
* Candy Show Available Rooms
*
* Authors:
* - Jonatan Männchen <jonatan.maennchen@amiadogroup.com>
*
* Copyright:
* - (c) 2012 Amiado Group AG. All rights reserved.
*/
var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.AvailableRooms = (function(self, Candy, $) {
/** Object: about
* About Game API
*
* Contains:
* (String) name - Candy Plugin Available Rooms
* (Float) version - andy Plugin Available Rooms version
*/
self.about = {
name: 'Candy Plugin Available Rooms',
version: '1.0.1'
};
/** Array: rooms
* all rooms
*
* Contains:
* (Object List) rooms
* (String) jid
* (String) name
* (Integer) person
*/
self.rooms = new Array();
/** Function: init
* Initializes the available-rooms plugin with the default settings.
*/
self.init = function(){
$(Candy.Core.Event).on('candy:core.chat.connection', function(e, args) {
if(args.status = Strophe.Status.ATTACHED) {
// Load rooms
self.loadRooms();
// Do it again all 10 seconds
setInterval(self.loadRooms, 10000);
}
});
// Add Handler
$(Candy.View.Pane).bind('candy:view.message.beforeSend', function(e, args) {
// (strip colors)
// if it matches '/list', show rooms and don't send anything
if (args.message.replace(/\|c:\d+\|/, '').toLowerCase() == '/list') {
self.showRooms();
args.message = '';
}
});
$(Candy.View.Pane).bind('candy:view.room.afterAdd', function(e, args) {
self.loadRooms();
});
};
/** Function: loadRooms
* Load all public rooms
*/
self.loadRooms = function () {
Candy.Core.getConnection().muc.listRooms('conference.' + Candy.Core.getConnection().domain, function(roomsData) {
CandyShop.AvailableRooms.rooms = new Array();
$.each($(roomsData).find('item'), function(item, room) {
var allreadyIn = false;
$.each(Candy.Core.getRooms(), function(item, roomSearch) {
if(roomSearch.getJid() == $(room).attr('jid')) {
allreadyIn = true;
return false;
}
});
if(!allreadyIn) {
CandyShop.AvailableRooms.rooms.push({
jid: $(room).attr('jid'),
name: $(room).attr('name').substr(0, $(room).attr('name').indexOf('(') - 1),
people: $(room).attr('name').substr($(room).attr('name').indexOf('(') + 1, $(room).attr('name').length - $(room).attr('name').indexOf('(') - 2)
});
}
});
CandyShop.AvailableRooms.rooms = CandyShop.AvailableRooms.rooms.sort(function(a, b) {
if(a.people == b.people) {
return a.name < b.name ? -1 : 1;
} else {
return a.people < b.people ? 1 : -1;
}
});
CandyShop.AvailableRooms.placePlusTab();
});
};
/** Function: placePlusTab
* placeTheTab
*/
self.placePlusTab = function() {
if(self.rooms.length > 0) {
if($('#add-room').length > 0) {
$('#add-room').parent().remove();
}
$('#chat-tabs').children().last().after('<li class="roomtype-add"><a id="add-room" href="javascript:;" class="label" style="padding-right: 10px;">+</a></li>');
$('#add-room').click(function(e) {
self.showRooms();
});
} else {
if($('#add-room').length > 0) {
$('#add-room').parent().remove();
}
}
};
/** Function: showRooms
* Show all public rooms
*/
self.showRooms = function() {
// get the element
elem = $('#add-room');
// blur the field
elem.blur();
// get the necessary items
var menu = $('#context-menu'),
content = $('ul', menu);
// clear the content if needed
content.empty();
// add the matches to the list
for(var i in self.rooms) {
content.append('<li class="available-room-option" data-jid="'+ self.rooms[i].jid +'">' + self.rooms[i].name + ' (' + self.rooms[i].people + ' Personen)</li>');
}
content.find('li').click(self.joinChanel);
var pos = elem.offset(),
posLeft = Candy.Util.getPosLeftAccordingToWindowBounds(menu, pos.left + 7),
posTop = Candy.Util.getPosTopAccordingToWindowBounds(menu, pos.top);
menu.css({'left': posLeft.px, 'top': '7px', backgroundPosition: posLeft.backgroundPositionAlignment + ' ' + posTop.backgroundPositionAlignment});
menu.fadeIn('fast');
};
/** Function: joinChanel
* Show all public rooms
*
* Parameters:
* (Event) e
*/
self.joinChanel = function(e) {
$('#context-menu').hide();
Candy.Core.Action.Jabber.Room.Join($(e.currentTarget).attr('data-jid'));
if($('#add-room').length > 0) {
$('#add-room').parent().remove();
}
e.preventDefault();
};
return self;
}(CandyShop.AvailableRooms || {}, Candy, jQuery));
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
# 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
});
```
\ No newline at end of file
/** File: candy.js
* Candy - Chats are not dead yet.
*
* Authors:
* - Troy McCabe <troy.mccabe@geeksquad.com>
*
* Copyright:
* (c) 2012 Geek Squad. All rights reserved.
*/
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));
\ No newline at end of file
# Clear chat plugin
This plugin adds a "clear chat" button, as well as the ability to type `/clear` to clear the current chat pane
### Usage
<script type="text/javascript" src="path_to_plugins/clearchat/candy.js"></script>
<link rel="stylesheet" type="text/css" href="path_to_plugins/clearchat/candy.css" />
...
CandyShop.ClearChat.init();
### Example configurations
// Show the clear chat button
CandyShop.ClearChat.init(true);
// Only allow `/clear`, do not show the button
CandyShop.ClearChat.init(false);
\ No newline at end of file
...@@ -34,7 +34,7 @@ CandyShop.ClearChat = (function(self, Candy, $) { ...@@ -34,7 +34,7 @@ CandyShop.ClearChat = (function(self, Candy, $) {
}); });
} }
$(Candy.View.Pane).on('candy:view.message.before-send', function(e, args) { $(Candy).on('candy:view.message.before-send', function(e, args) {
// (strip colors) // (strip colors)
// if it matches '/clear', clear the chat window and don't send anything // if it matches '/clear', clear the chat window and don't send anything
if (args.message.replace(/\|c:\d+\|/, '').toLowerCase() == '/clear') { if (args.message.replace(/\|c:\d+\|/, '').toLowerCase() == '/clear') {
......
...@@ -10,14 +10,14 @@ CandyShop.Colors = (function(self, Candy, $) { ...@@ -10,14 +10,14 @@ CandyShop.Colors = (function(self, Candy, $) {
self.applyTranslations(); self.applyTranslations();
$(Candy.View.Pane).on('candy:view.message.before-send', function(e, args) { $(Candy).on('candy:view.message.before-send', function(e, args) {
if(_currentColor > 0 && $.trim(args.message) !== '') { if(_currentColor > 0 && $.trim(args.message) !== '') {
args.message = '|c:'+ _currentColor +'|' + args.message; args.message = '|c:'+ _currentColor +'|' + args.message;
} }
}); });
$(Candy.View.Pane).on('candy:view.message.before-show', function(e, args) { $(Candy).on('candy:view.message.before-render', function(e, args) {
args.message = args.message.replace(/^\|c:([0-9]{1,2})\|(.*)/gm, '<span class="colored color-$1">$2</span>'); 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')) { if(Candy.Util.cookieExists('candyshop-colors-current')) {
...@@ -65,12 +65,20 @@ CandyShop.Colors = (function(self, Candy, $) { ...@@ -65,12 +65,20 @@ CandyShop.Colors = (function(self, Candy, $) {
}; };
self.applyTranslations = function() { self.applyTranslations = function() {
Candy.View.Translation.en.candyshopColorsMessagecolor = 'Message color'; var translations = {
Candy.View.Translation.ru.candyshopColorsMessagecolor = 'Цвет сообщения'; 'en' : 'Message Color',
Candy.View.Translation.de.candyshopColorsMessagecolor = 'Farbe für Nachrichten'; 'ru' : 'Цвет сообщения',
Candy.View.Translation.fr.candyshopColorsMessagecolor = 'Couleur des messages'; 'de' : 'Farbe für Nachrichten',
Candy.View.Translation.nl.candyshopColorsMessagecolor = 'Berichtkleur'; 'fr' : 'Couleur des messages',
Candy.View.Translation.es.candyshopColorsMessagecolor = 'Color de los mensajes'; '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; return self;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* inline-images * inline-images
* @version 1.0 * @version 1.0
* @author Manuel Alabor (manuel@alabor.me) * @author Manuel Alabor (manuel@alabor.me)
* @author Jonatan Männchen <jonatan@maennchen.ch>
* *
* If a user posts a URL to an image, that image gets rendered directly * If a user posts a URL to an image, that image gets rendered directly
* inside of Candy. * inside of Candy.
...@@ -12,16 +13,14 @@ var CandyShop = (function(self) { return self; }(CandyShop || {})); ...@@ -12,16 +13,14 @@ var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.InlineImages = (function(self, Candy, $) { CandyShop.InlineImages = (function(self, Candy, $) {
var _fileExtensions = ['png','jpg','jpeg','gif'] var _fileExtensions = ['png','jpg','jpeg','gif']
,_originalLinkify = Candy.Util.Parser.linkify
,_maxImageSize = 100; ,_maxImageSize = 100;
/** Function: init /** Function: init
* Initializes the inline-images plugin with the default settings. * Initializes the inline-images plugin with the default settings.
*/ */
self.init = function() { self.init = function() {
$(Candy.View.Pane).on('candy:view.message.before-show', handleBeforeShow); $(Candy).on('candy:view.message.before-show', handleBeforeShow);
$(Candy.View.Pane).on('candy:view.message.after-show', handleOnShow); $(Candy).on('candy:view.message.after-show', handleOnShow);
Candy.Util.Parser.linkify = linkify;
}; };
/** Function: initWithFileExtensions /** Function: initWithFileExtensions
...@@ -33,7 +32,7 @@ CandyShop.InlineImages = (function(self, Candy, $) { ...@@ -33,7 +32,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
*/ */
self.initWithFileExtensions = function(fileExtensions) { self.initWithFileExtensions = function(fileExtensions) {
_fileExtensions = fileExtensions; _fileExtensions = fileExtensions;
init(); self.init();
}; };
/** Function: initWithMaxImageSize /** Function: initWithMaxImageSize
...@@ -45,7 +44,7 @@ CandyShop.InlineImages = (function(self, Candy, $) { ...@@ -45,7 +44,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
*/ */
self.initWithMaxImageSize = function(maxImageSize) { self.initWithMaxImageSize = function(maxImageSize) {
_maxImageSize = maxImageSize; _maxImageSize = maxImageSize;
init(); self.init();
}; };
/** Function: initWithFileExtensionsAndMaxImageSize /** Function: initWithFileExtensionsAndMaxImageSize
...@@ -60,7 +59,7 @@ CandyShop.InlineImages = (function(self, Candy, $) { ...@@ -60,7 +59,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
self.initWithFileExtensionsAndMaxImageSize = function(fileExtensions, maxImageSize) { self.initWithFileExtensionsAndMaxImageSize = function(fileExtensions, maxImageSize) {
_fileExtensions = fileExtensions; _fileExtensions = fileExtensions;
_maxImageSize = maxImageSize; _maxImageSize = maxImageSize;
init(); self.init();
}; };
...@@ -75,9 +74,7 @@ CandyShop.InlineImages = (function(self, Candy, $) { ...@@ -75,9 +74,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
*/ */
var handleBeforeShow = function(e, args) { var handleBeforeShow = function(e, args) {
var message = args.message; var message = args.message;
var processed = message.replace(/\|[^\|]+\|/, ""); var processed = message.replace(/>(\b(https?|ftp|file):\/\/[\-A-Z0-9+&@#\/%?=~_|!:,.;]*[\-A-Z0-9+&@#\/%=~_|])</ig, replaceCallback);
processed = processed.replace(/(^|[^\/])(www|i\.[^\.]+\.[\S]+(\b|$))/gi, '$1http://$2');
processed = processed.replace(/\b(https?:\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, replaceCallback);
args.message = processed; args.message = processed;
return processed; return processed;
}; };
...@@ -113,22 +110,7 @@ CandyShop.InlineImages = (function(self, Candy, $) { ...@@ -113,22 +110,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
imageLoader.src = url; imageLoader.src = url;
}); });
} };
/** Function: linkify
* Is used to overwrite the original Candy.Util.Parser.linkify.
* This implementation prevents the parsing of URL's by the Candy core.
* inline-images handles this on itself by handleBeforeShow.
*
* Parameters:
* (String) text - text to process
*
* Returns:
* (String)
*/
var linkify = function(text) {
return text;
}
/** Function: replaceCallback /** Function: replaceCallback
* This callback handles matches from the URL regex. * This callback handles matches from the URL regex.
...@@ -136,25 +118,23 @@ CandyShop.InlineImages = (function(self, Candy, $) { ...@@ -136,25 +118,23 @@ CandyShop.InlineImages = (function(self, Candy, $) {
* indicator. If it is just a common URL, a link-tag gets returned. * indicator. If it is just a common URL, a link-tag gets returned.
* *
* Paramters: * Paramters:
* (String) uselessMatch - whole url with ">" and "<"
* (String) match - matched URL * (String) match - matched URL
* *
* Returns: * Returns:
* (String) * (String)
*/ */
var replaceCallback = function(match) { var replaceCallback = function(uselessMatch, match) {
var result = match; var result = match;
var dotPosition = match.lastIndexOf("."); var dotPosition = match.lastIndexOf(".");
if(dotPosition > -1) { if(dotPosition > -1) {
if(_fileExtensions.indexOf(match.substr(dotPosition+1)) != -1) { if(_fileExtensions.indexOf(match.substr(dotPosition+1)) != -1) {
result = buildImageLoaderSource(match); result = buildImageLoaderSource(match);
} else {
result = buildLinkSource(match);
} }
} }
return '>' + result + '<';
return result; };
}
/** Function: buildImageLoaderSource /** Function: buildImageLoaderSource
* Returns a loader indicator. The handleOnShow method fullfills afterwards * Returns a loader indicator. The handleOnShow method fullfills afterwards
...@@ -167,8 +147,8 @@ CandyShop.InlineImages = (function(self, Candy, $) { ...@@ -167,8 +147,8 @@ CandyShop.InlineImages = (function(self, Candy, $) {
* (String) * (String)
*/ */
var buildImageLoaderSource = function(url) { var buildImageLoaderSource = function(url) {
return '<img class="inlineimages-loader" longdesc="' + url + '" src="candy-plugins/inline-images/spinner.gif" />' return '<img class="inlineimages-loader" longdesc="' + url + '" src="candy-plugins/inline-images/spinner.gif" />';
} };
/** Function: buildImageSource /** Function: buildImageSource
* Returns HTML source to show a URL as an image. * Returns HTML source to show a URL as an image.
...@@ -181,20 +161,7 @@ CandyShop.InlineImages = (function(self, Candy, $) { ...@@ -181,20 +161,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
*/ */
var buildImageSource = function(url, width, height) { var buildImageSource = function(url, width, height) {
return '<a href="' + url + '" target="_blank" class="inlineimages-link"><img src="' + url + '" width="' + width + '" height="' + height + '"/></a>'; return '<a href="' + url + '" target="_blank" class="inlineimages-link"><img src="' + url + '" width="' + width + '" height="' + height + '"/></a>';
} };
/** Function: buildLinkSource
* Returns HTML source to show a URL as a link.
*
* Parameters:
* (String) url - url
*
* Returns:
* (String)
*/
var buildLinkSource = function(url) {
return '<a href="' + url + '" target="_blank">' + url + '</a>';
}
return self; return self;
}(CandyShop.InlineImages || {}, Candy, jQuery)); }(CandyShop.InlineImages || {}, Candy, jQuery));
# Inline Videos Plugin
If a user posts a URL to a youtube video, that video gets rendered directly inside of Candy.
## Usage
Include the JavaScript file:
```HTML
<script type="text/javascript" src="path_to_plugins/inline-videos/candy.js"></script>
```
Call its `init()` method after Candy has been initialized:
```JavaScript
Candy.init('/http-bind/');
CandyShop.InlineVideos.init();
Candy.Core.connect();
```
\ No newline at end of file
/** File: candy.js
* Candy - Chats are not dead yet.
*
* Authors
* - Jonatan Männchen <jonatan.maennchen@amiadogroup.com>
*/
var CandyShop = (function(self) { return self; }(CandyShop || {}));
/** Class: InlineVideos
* If a user posts a URL to a video, that video gets rendered directly
* inside of Candy.
*/
CandyShop.InlineVideos = (function(self, Candy, $) {
/** Function: init
* Initializes the inline-videos plugin with the default settings.
*
* Parameters:
* (Object) options - An options packet to apply to this plugin
*/
self.init = function(options) {
// add a listener to these events
$(Candy.View.Pane).on('candy:view.message.beforeShow', self.handleBeforeShow);
};
/** Function: handleBeforeShow
* Handles the beforeShow event of a message.
*
* Parameters:
* (String) message - the message to process
*
* Returns:
* (String)
*/
self.handleBeforeShow = function(e, args) {
args.message = args.message.replace(/\>(https?:\/\/w{0,3}\.?youtube.com\/watch\?v=([^\s^&]*)([^\s]*))\<\/a\>/i, '>$1<br /><iframe width="300" height="200" src="http://www.youtube.com/embed/$2" frameborder="0" allowfullscreen></iframe></a><br />');
};
return self;
}(CandyShop.InlineVideos || {}, Candy, jQuery));
# Join Plugin
A plugin to join public rooms.
Just Type `/join room [password]` to join a room.
## Usage
Include the JavaScript file::
```HTML
<script type="text/javascript" src="candyshop/join/candy.js"></script>
```
To enable the Join Plugin, just add one of the ´init´ methods to your bootstrap:
```JavaScript
CandyShop.Join.init();
```
/** File: candy.js
* Candy join room over command
*
* Authors:
* - Jonatan Männchen <jonatan.maennchen@amiadogroup.com>
*
* Copyright:
* - (c) 2012 Amiado Group AG. All rights reserved.
*/
var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.Join = (function(self, Candy, $) {
/** Object: about
* About Game API
*
* Contains:
* (String) name - Candy Plugin Join
* (Float) version - andy Plugin Available Rooms version
*/
self.about = {
name: 'Candy Plugin Join',
version: '1.0.2'
};
/** Function: init
* Initializes the join plugin with the default settings.
*/
self.init = function(){
$(Candy).bind('candy:view.message.before-send', function(e, args) {
// (strip colors)
// if it matches '/join', join room and don't send anything
if (args.message.replace(/\|c:\d+\|/, '').substring(0, 5).toLowerCase() == '/join') {
self.joinRoom(args.message.replace(/\|c:\d+\|/, '').substring(6).toLowerCase());
args.message = '';
}
});
};
/** Function: joinRoom
* Join a room
*
* Parameters:
* (String) args
*/
self.joinRoom = function(args) {
args = args.split(' ');
if(typeof args[0] != 'undefined' && typeof args[1] != 'undefined') {
Candy.Core.Action.Jabber.Room.Join(args[0] + '@conference.' + Candy.Core.getConnection().domain, args[1]);
} else if(typeof args[0] != 'undefined' && args[0] != '') {
Candy.Core.Action.Jabber.Room.Join(args[0] + '@conference.' + Candy.Core.getConnection().domain);
}
};
return self;
}(CandyShop.Join || {}, Candy, jQuery));
...@@ -2,16 +2,17 @@ ...@@ -2,16 +2,17 @@
This plugin will complete the names of users in the room when a specified key is pressed. This plugin will complete the names of users in the room when a specified key is pressed.
### Usage ### Usage
<script type="text/javascript" src="path_to_plugins/gs-namecomplete/candy.js"></script> <script type="text/javascript" src="path_to_plugins/namecomplete/candy.js"></script>
<link rel="stylesheet" type="text/css" href="path_to_plugins/gs-namecomplete/candy.css" /> <link rel="stylesheet" type="text/css" href="path_to_plugins/namecomplete/candy.css" />
... ...
CandyShop.NameComplete.init(); CandyShop.NameComplete.init();
### Configuration options ### Configuration options
nameIdentifier - String - The identifier to look for in a string. Defaults to '@' `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)
`completeKeyCode` - Integer - The key code of the key to use. Defaults to `9` (tab)
### Example configurations ### Example configurations
......
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
background-color: #ccc; background-color: #ccc;
} }
#context-menu li.gs-namecomplete-option { #context-menu li.candy-namecomplete-option {
padding: 3px 5px; padding: 3px 5px;
} }
\ No newline at end of file
...@@ -29,6 +29,11 @@ CandyShop.NameComplete = (function(self, Candy, $) { ...@@ -29,6 +29,11 @@ CandyShop.NameComplete = (function(self, Candy, $) {
*/ */
var _nicks = []; var _nicks = [];
/** String: _selector
* The selector for the visible message box
*/
var _selector = 'input[name="message"]:visible';
/** Function: init /** Function: init
* Initialize the NameComplete plugin * Initialize the NameComplete plugin
* Show options for auto completion of names * Show options for auto completion of names
...@@ -41,7 +46,7 @@ CandyShop.NameComplete = (function(self, Candy, $) { ...@@ -41,7 +46,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
$.extend(true, _options, options); $.extend(true, _options, options);
// listen for keydown when autocomplete options exist // listen for keydown when autocomplete options exist
$(document).on('keydown', 'input[name="message"]', function(e) { $(document).on('keydown', _selector, function(e) {
// if we hear the key code for completion // if we hear the key code for completion
if (e.which == _options.completeKeyCode) { if (e.which == _options.completeKeyCode) {
// update the list of nicks to grab // update the list of nicks to grab
...@@ -121,7 +126,7 @@ CandyShop.NameComplete = (function(self, Candy, $) { ...@@ -121,7 +126,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
} }
// remove the listener on the field // remove the listener on the field
$('input[name="message"]').unbind('keydown', self.keyDown); $(_selector).unbind('keydown', self.keyDown);
// hide the menu // hide the menu
menu.hide(); menu.hide();
...@@ -142,7 +147,7 @@ CandyShop.NameComplete = (function(self, Candy, $) { ...@@ -142,7 +147,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
self.replaceName($(e.currentTarget).text()); self.replaceName($(e.currentTarget).text());
$(document).unbind('keydown', self.keyDown); $(document).unbind('keydown', self.keyDown);
$('#context-menu').hide(); $('#context-menu').hide();
$('input[name="message"]').focus(); $(_selector).focus();
e.preventDefault(); e.preventDefault();
}; };
...@@ -154,20 +159,24 @@ CandyShop.NameComplete = (function(self, Candy, $) { ...@@ -154,20 +159,24 @@ CandyShop.NameComplete = (function(self, Candy, $) {
_nicks = []; _nicks = [];
// grab the roster in the current room // grab the roster in the current room
var roster = Candy.Core.getRoom(Candy.View.getCurrent().roomJid).getRoster().getAll(); 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 // iterate and add the nicks to the collection
$.each(roster, function(index, item) { $.each(roster, function(index, item) {
_nicks.push(_options.nameIdentifier + item.getNick()); _nicks.push(_options.nameIdentifier + item.getNick());
}); });
} }
}
/** Function: replaceName /** Function: replaceName
* *
*/ */
self.replaceName = function(replaceText) { self.replaceName = function(replaceText) {
// get the parts of the message // get the parts of the message
var msgParts = $('input[name="message"]').val().split(' '); var $msgBox = $(_selector);
var msgParts = $msgBox.val().split(' ');
// If the name is the first word, add a colon to the end // If the name is the first word, add a colon to the end
if (msgParts.length==1) { if (msgParts.length==1) {
...@@ -180,7 +189,7 @@ CandyShop.NameComplete = (function(self, Candy, $) { ...@@ -180,7 +189,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
msgParts[msgParts.length - 1] = replaceText; msgParts[msgParts.length - 1] = replaceText;
// put the string back together on spaces // put the string back together on spaces
$('input[name="message"]').val(msgParts.join(' ')); $msgBox.val(msgParts.join(' '));
} }
/** Function: showPicker /** Function: showPicker
...@@ -201,7 +210,7 @@ CandyShop.NameComplete = (function(self, Candy, $) { ...@@ -201,7 +210,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
// add the matches to the list // add the matches to the list
for(i = 0; i < matches.length; i++) { for(i = 0; i < matches.length; i++) {
content.append('<li class="gs-namecomplete-option">' + matches[i] + '</li>'); content.append('<li class="candy-namecomplete-option">' + matches[i] + '</li>');
} }
// select the first item // select the first item
...@@ -210,7 +219,7 @@ CandyShop.NameComplete = (function(self, Candy, $) { ...@@ -210,7 +219,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
content.find('li').click(self.selectOnClick); content.find('li').click(self.selectOnClick);
// bind the keydown to move around the menu // bind the keydown to move around the menu
$('input[name="message"]').bind('keydown', self.keyDown); $(_selector).bind('keydown', self.keyDown);
// estimate the left to the # of chars * 7...not sure? // estimate the left to the # of chars * 7...not sure?
// get the top of the box to put this thing at // get the top of the box to put this thing at
......
# 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.
\ No newline at end of file
#nickchange-control {
background: no-repeat url('nickchange-control.png');
}
\ No newline at end of file
/**
* Nickchange plugin for Candy
*
* Copyright 2014 Michael Weibel <michael.weibel@gmail.com>
*
* License: MIT
*/
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(event) {
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));
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
# 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();
```
/*
* HTML5 Notifications
* @version 1.0
* @author Jonatan Männchen <jonatan@maennchen.ch>
*
* Notify user if new messages come in.
*/
var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.Notifications = (function(self, Candy, $) {
/** Function: init
* Initializes the notifications plugin.
*
* @return void
*/
self.init = function() {
// Just init if notifications are supported
if (window.webkitNotifications) {
// Setup Permissions (has to be kicked on with some user-events)
jQuery(document).one('click keydown', setupPermissions);
// Add Listener for Notifications
$(Candy).on('candy:view.message.after-show', handleOnShow);
}
};
/** Function: checkPermissions
* Check if the plugin has permission to send notifications.
*
* @return boid
*/
function setupPermissions() {
// Check if permissions is given
if (window.webkitNotifications.checkPermission() != 0) { // 0 is PERMISSION_ALLOWED
// Request for it
window.webkitNotifications.requestPermission();
}
};
/** Function: handleOnShow
* Descriptions
*
* Parameters:
* (Array) args
*
* @return void
*/
function handleOnShow(e, args) {
// Check if window has focus, so no notification needed
if (!document.hasFocus()) {
// Check if notifications are allowed
if (window.webkitNotifications.checkPermission() == 0) { // 0 is PERMISSION_ALLOWED
// Send Notification
var notification = window.webkitNotifications.createNotification(
window.location + '/' + Candy.View.getOptions().resources + '/img/favicon.png',
args.name,
args.message);
notification.show();
}
}
};
return self;
}(CandyShop.Notifications || {}, Candy, jQuery));
# Notify me plugin
This plugin will notify users when their names are mentioned and prefixed with a specific token
### Usage
<script type="text/javascript" src="path_to_plugins/notifyme/candy.js"></script>
<link rel="stylesheet" type="text/css" href="path_to_plugins/notifyme/candy.css" />
...
CandyShop.NotifyMe.init();
### Configuration options
`nameIdentifier` - String - The identifier to look for in a string. Defaults to `'@'`
`playSound` - Boolean - Whether to play a sound when the username is mentioned. Defaults to `true`
`highlightInRoom` - Boolean - Whether to highlight the username when it is mentioned. Defaults to `true`
### Example configurations
// Highlight my name when it's prefixed with a '+'
CandyShop.NotifyMe.init({
nameIdentifier: '+',
playSound: false
});
// Highlight and play a sound if my name is prefixed with a '-'
CandyShop.NotifyMe.init({
nameIdentifier: '-'
});
\ No newline at end of file
.gs-notifyme-highlight { .candy-notifyme-highlight {
background: #FFFF00; background: #FFFF00;
} }
\ No newline at end of file
...@@ -39,18 +39,18 @@ CandyShop.NotifyMe = (function(self, Candy, $) { ...@@ -39,18 +39,18 @@ CandyShop.NotifyMe = (function(self, Candy, $) {
$.extend(true, _options, options); $.extend(true, _options, options);
// bind to the beforeShow event // bind to the beforeShow event
$(Candy.View.Pane).on('candy:view.message.before-show', function(e, args) { $(Candy).on('candy:view.message.before-render', function(e, args) {
// get the nick from the current user // get the nick from the current user
var nick = Candy.Core.getUser().getNick(); var nick = Candy.Core.getUser().getNick();
// make it what is searched // make it what is searched
// search for the name at the beginning of the message, or with a space in front // search for <identifier>name in the whole message
var searchTerm = _options.nameIdentifier + nick; var searchTerm = _options.nameIdentifier + nick;
var searchRegExp = new RegExp('(^' + searchTerm + '| ' + searchTerm + ')', 'ig'); var searchRegExp = new RegExp('^(.*)(' + searchTerm + '| ' + searchTerm + ')', 'ig');
// if it's in the message and it's not from me, do stuff // if it's in the message and it's not from me, do stuff
// I wouldn't want to say 'just do @{MY_NICK} to get my attention' and have it knock... // I wouldn't want to say 'just do @{MY_NICK} to get my attention' and have it knock...
if (searchRegExp.test(args.message) && args.nick != nick) { if (searchRegExp.test(args.templateData.message) && args.templateData.name != nick) {
// play the sound if specified // play the sound if specified
if (_options.playSound) { if (_options.playSound) {
Candy.View.Pane.Chat.Toolbar.playSound(); Candy.View.Pane.Chat.Toolbar.playSound();
...@@ -58,7 +58,7 @@ CandyShop.NotifyMe = (function(self, Candy, $) { ...@@ -58,7 +58,7 @@ CandyShop.NotifyMe = (function(self, Candy, $) {
// highlight if specified // highlight if specified
if (_options.highlightInRoom) { if (_options.highlightInRoom) {
args.message = args.message.replace(searchRegExp, '<span class="gs-notifyme-highlight">' + searchTerm + '</span>'); args.templateData.message = args.templateData.message.replace(searchRegExp, '$1<span class="candy-notifyme-highlight">' + searchTerm + '</span>');
} }
} }
}); });
......
# Automatic entry box refocus
This plugin puts the focus on the entry box if the user clicks somewhere in the message list.
## Usage
```HTML
<script type="text/javascript" src="candyshop/refocus/candy.js"></script>
```
```JavaScript
CandyShop.Refocus.init();
```
\ No newline at end of file
/*
* candy-refocus-plugin
* @version 1.0 (2014-01-26)
* @author warcode (github.com/warcode)
*
* This plugin puts the focus on the entry box if the user clicks in the message window/list.
*/
var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.Refocus = (function(self, Candy, $) {
self.init = function(options) {
Candy.Core.log('[Refocus] init');
$(Candy.View.Pane).on('candy:view.room.after-show', roomAfterShow);
};
function roomAfterShow(e, args) {
Candy.Core.log('[Refocus] roomAfterShow');
try {
$('.message-pane-wrapper').mousedown(function() {
$('.message-form').children(".field")[0].focus();
return false;
});
} catch (e) {
Candy.Core.log('[Refocus] jQuery exception:');
Candy.Core.log(e);
}
}
return self;
}(CandyShop.Refocus || {}, Candy, jQuery));
# Remove ignore plugin
This plugin will remove the option to ignore another user
## Usage
Include the JavaScript file:
```HTML
<script type="text/javascript" src="path_to_plugins/removeignore/candy.js"></script>
```
Call its `init()` method after Candy has been initialized:
```JavaScript
Candy.init('/http-bind/');
CandyShop.RemoveIgnore.init();
Candy.Core.connect();
```
\ No newline at end of file
/** File: candy.js
* Candy - Chats are not dead yet.
*
* Authors:
* - Troy McCabe <troy.mccabe@geeksquad.com>
*
* Copyright:
* (c) 2012 Geek Squad. All rights reserved.
*/
var CandyShop = (function(self) { return self; }(CandyShop || {}));
/** Class: CandyShop.RemoveIgnore
* Remove the ignore option in the roster
*/
CandyShop.RemoveIgnore = (function(self, Candy, $) {
/** Function: init
* Initialize this plugin to remove the ignore option
*/
self.init = function() {
// bind to the contextmenu event so we can modify the links
$(Candy).bind('candy:view.roster.context-menu', function(e, args) {
// override the ignore so that nobody has permission
args.menulinks.ignore = {
requiredPermission: function(user, me) { return false; }
};
});
};
return self;
}(CandyShop.RemoveIgnore || {}, Candy, jQuery));
\ No newline at end of file
.message-pane dt { .message-pane li.mention,
height: 13px; .message-pane li.mention small {
}
.message-pane dd.mention, .message-pane dt.mention {
background-color: #FFF7DE; background-color: #FFF7DE;
} }
\ No newline at end of file
/* /*
* candy-replies-plugin * candy-replies-plugin
* @version 0.1 (2013-2-20) * @version 0.2 (2014-01-05)
* @author Drew Harry (drew.harry@gmail.com) * @author Drew Harry (drew.harry@gmail.com)
* *
* Adds @reply highlighting to chat messages to help with high velocity * Adds @reply highlighting to chat messages to help with high velocity
...@@ -13,12 +13,12 @@ CandyShop.Replies = (function(self, Candy, $) { ...@@ -13,12 +13,12 @@ CandyShop.Replies = (function(self, Candy, $) {
self.init = function() { self.init = function() {
Candy.View.Event.Message.onShow = handleOnShow; $(Candy).on('candy:view.message.after-show', handleOnShow);
return self; return self;
}; };
var handleOnShow = function(args) { var handleOnShow = function(e, args) {
var localNick = Candy.Core.getUser().getNick(); var localNick = Candy.Core.getUser().getNick();
var re = new RegExp("@" + localNick + "([ .!><\":\/@-]|$)", 'im'); var re = new RegExp("@" + localNick + "([ .!><\":\/@-]|$)", 'im');
...@@ -27,7 +27,6 @@ CandyShop.Replies = (function(self, Candy, $) { ...@@ -27,7 +27,6 @@ CandyShop.Replies = (function(self, Candy, $) {
var el = args.element; var el = args.element;
el.addClass("mention"); el.addClass("mention");
el.prev().addClass("mention");
} }
} }
......
...@@ -14,28 +14,35 @@ To enable *RoomPanel* you have to include its JavaScript code and stylesheet: ...@@ -14,28 +14,35 @@ To enable *RoomPanel* you have to include its JavaScript code and stylesheet:
Call its `init()` method after Candy has been initialized: Call its `init()` method after Candy has been initialized:
```JavaScript ```JavaScript
Candy.init('/http-bind/'); Candy.init('/http-bind/', {core: {autojoin: []}});
// enable RoomPanel plugin // enable RoomPanel plugin
CandyShop.RoomPanel.init({ CandyShop.RoomPanel.init({
// domain that hosts the muc rooms, only required if autoDetectRooms is enabled // domain that hosts the muc rooms, only required if autoDetectRooms is enabled
mucDomain: 'conference.yourdomain.com', mucDomain: 'conference.example.com',
// allow you to force a list of rooms, only required if autoDetectRoom is disabled // allow you to force a list of rooms, only required if autoDetectRoom is disabled
roomList: [ roomList: [
{ {
name: 'my room', name: 'my room',
jid: 'my-room@conference.yourdomain.com' jid: 'my-room@conference.example.com'
}, },
{ {
name: 'other room', name: 'other room',
jid: 'other-room@conference.yourdomain.com' jid: 'other-room@conference.example.com'
} }
], ],
// show room list if all rooms are closed, default value is true. [optional] // show room list if all rooms are closed, default value is true. [optional]
showIfAllTabClosed: true, showIfAllTabClosed: true,
// show '+' at the end of the room tabs
showTab: true,
// show icon in toolbar to show room list
showToolbarIcon: false,
// detect rooms before showing list, default value is true. [optional] // detect rooms before showing list, default value is true. [optional]
autoDetectRooms: true, autoDetectRooms: true,
...@@ -44,4 +51,5 @@ CandyShop.RoomPanel.init({ ...@@ -44,4 +51,5 @@ CandyShop.RoomPanel.init({
}); });
Candy.Core.connect(); Candy.Core.connect();
```
.roomList a { .roomList a {
color: #AAA; color: #333;
} }
#roomPanel-control { #roomPanel-control {
width: 16px; width: 16px;
background: url(images/room.png); background: url(images/room.png);
} }
#roomPanel-tab {
font-weight: bold;
font-size: 20px;
line-height: 23px;
}
#roomPanel-tab a {
padding: 0px 10px 10px;
}
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
...@@ -18,7 +18,13 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) { ...@@ -18,7 +18,13 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
autoDetectRooms: true, autoDetectRooms: true,
// how long in seconds before refreshing room list, default value is 600. [optional] // how long in seconds before refreshing room list, default value is 600. [optional]
roomCacheTime: 600 roomCacheTime: 600,
// show a "+"-tab to access the panel
showTab: true,
// show toolbar icon to access the panel
showToolbarIcon: false
}; };
...@@ -30,29 +36,42 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) { ...@@ -30,29 +36,42 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
/* Overwrite candy allTabsClosed function not /* Overwrite candy allTabsClosed function not
* to disconnect when all tags are closed */ * to disconnect when all tabs are closed */
if (_options.showIfAllTabClosed) { if (_options.showIfAllTabClosed) {
Candy.View.Pane.Chat.allTabsClosed = function () { Candy.View.Pane.Chat.allTabsClosed = function () {
CandyShop.RoomPanel.showRoomPanel(); self.showRoomPanel();
return; return;
}; };
} //if } //if
if(_options.showToolbarIcon !== false) {
var html = '<li id="roomPanel-control" data-tooltip="' + $.i18n._('candyshopRoomPanelListRoom') + '"></li>'; var html = '<li id="roomPanel-control" data-tooltip="' + $.i18n._('candyshopRoomPanelListRoom') + '"></li>';
$('#chat-toolbar').prepend(html); $('#chat-toolbar').prepend(html);
$('#roomPanel-control').click(function() { $('#roomPanel-control').click(function() {
CandyShop.RoomPanel.showRoomPanel(); self.showRoomPanel();
}); });
}
if(_options.showTab === true) {
var chatTabs = $('#chat-tabs'),
html = '<li id="roomPanel-tab"><a href="#" class="label">+</a></li>';
chatTabs.append(html);
var el = $('#roomPanel-tab');
$('#roomPanel-tab').click(function() {
self.showRoomPanel();
});
$(Candy).on('candy:view.room.after-add', function(_evt, args) {
chatTabs.remove('#roomPanel-tab').append(el);
});
}
$(Candy.Core.Event).on('candy:core.chat.connection', {update: function(obj, data) { $(Candy).on('candy:core.chat.connection', function(obj, data) {
if (data.type == 'connection') { if (Strophe.Status.CONNECTED == data.status ||
if (Strophe.Status.CONNECTED == data.status) { Strophe.Status.ATTACHED == data.status) {
/* only show room window if not already in a room, timeout is to let some time for auto join to execute */ /* only show room window if not already in a room, timeout is to let some time for auto join to execute */
setTimeout(CandyShop.RoomPanel.showRoomPanelIfAllClosed, 500); setTimeout(CandyShop.RoomPanel.showRoomPanelIfAllClosed, 500);
} //if } //if
} //if
return true; return true;
}}); });
}; };
...@@ -67,7 +86,7 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) { ...@@ -67,7 +86,7 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
} //for } //for
if (roomCount == 0) { if (roomCount == 0) {
CandyShop.RoomPanel.showRoomPanel(); self.showRoomPanel();
} //if } //if
} }
...@@ -83,8 +102,8 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) { ...@@ -83,8 +102,8 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
} //if } //if
newRoomList.push({ newRoomList.push({
name: name, name: Strophe.unescapeNode(name),
jid: jid jid: Candy.Util.unescapeJid(jid)
}); });
}); });
...@@ -103,7 +122,7 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) { ...@@ -103,7 +122,7 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
var timeDiff = Math.round(new Date().getTime() / 1000) - _options.roomCacheTime; var timeDiff = Math.round(new Date().getTime() / 1000) - _options.roomCacheTime;
if (_options.autoDetectRooms && timeDiff > _lastRoomUpdate ) { if (_options.autoDetectRooms && timeDiff > _lastRoomUpdate ) {
/* sends a request to get list of rooms user for the room */ /* sends a request to get list of rooms user for the room */
var iq = $iq({type: 'get', from: Candy.Core.getUser().getJid(), to: _options.mucDomain , id: 'findRooms1'}) var iq = $iq({type: 'get', from: Candy.Core.getUser().getJid(), to: _options.mucDomain})
.c('query', {xmlns: Strophe.NS.DISCO_ITEMS}); .c('query', {xmlns: Strophe.NS.DISCO_ITEMS});
Candy.Core.getConnection().sendIQ(iq, self.updateRoomList); Candy.Core.getConnection().sendIQ(iq, self.updateRoomList);
...@@ -130,21 +149,20 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) { ...@@ -130,21 +149,20 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
}; };
self.applyTranslations = function() { self.applyTranslations = function() {
Candy.View.Translation.en.candyshopRoomPanelListRoom = 'List Rooms'; var translations = {
Candy.View.Translation.ru.candyshopRoomPanelListRoom = 'Список комнат'; 'en' : ['List Rooms', 'Choose Room To Join'],
Candy.View.Translation.de.candyshopRoomPanelListRoom = 'Verfügbare Räume anzeigen'; 'ru' : ['Список комнат', 'Выберите комнату'],
Candy.View.Translation.fr.candyshopRoomPanelListRoom = 'Liste des salles'; 'de' : ['Verfügbare Räume anzeigen', 'Verfügbare Räume'],
Candy.View.Translation.nl.candyshopRoomPanelListRoom = 'List Rooms'; 'fr' : ['Choisir une salle', 'Liste des salles'],
Candy.View.Translation.es.candyshopRoomPanelListRoom = 'List Rooms'; 'nl' : ['Choose Room To Join', 'List Rooms'],
'es' : ['Choose Room To Join', 'List Rooms']
};
Candy.View.Translation.en.candyshopRoomPanelChooseRoom = 'Choose Room To Join'; $.each(translations, function(k, v) {
Candy.View.Translation.ru.candyshopRoomPanelChooseRoom = 'Выберите комнату '; if(Candy.View.Translation[k]) {
Candy.View.Translation.de.candyshopRoomPanelChooseRoom = 'Verfügbare Räume'; Candy.View.Translation[k].candyshopRoomPanelListRoom = v[0];
Candy.View.Translation.fr.candyshopRoomPanelChooseRoom = 'Choisir une salle'; Candy.View.Translation[k].candyshopRoomPanelChooseRoom = v[1];
Candy.View.Translation.nl.candyshopRoomPanelChooseRoom = 'Choose Room To Join'; }
Candy.View.Translation.es.candyshopRoomPanelChooseRoom = 'Choose Room To Join'; });
}; };
return self; return self;
......
This diff was suppressed by a .gitattributes entry.
# Sticky subject plugin
This plugin will retain the subject of the room at the top of the chat panel
![Sticky Subject](screenshot.png)
## Usage
Include the JavaScript and CSS files:
```HTML
<script type="text/javascript" src="path_to_plugins/stickysubject/candy.js"></script>
<link rel="stylesheet" type="text/css" href="path_to_plugins/stickysubject/candy.css" />
```
Call its `init()` method after Candy has been initialized:
```JavaScript
Candy.init('/http-bind/');
CandyShop.StickySubject.init();
Candy.Core.connect();
```
\ No newline at end of file
.candy-subject-container {
background: #FFFFFF;
border-bottom: 1px solid #CCCCCC;
left: 0;
margin: 30px 200px 0 0;
padding: 5px;
position: absolute;
right: 0;
top: 0;
}
.candy-subject-container > a {
color: #a00;
}
.candy-subject-container > a:hover {
text-decoration: underline;
}
.candy-has-subject {
margin: 55px 200px 32px 0;
}
\ No newline at end of file
/** File: candy.js
* Candy - Chats are not dead yet.
*
* Authors:
* - Troy McCabe <troy.mccabe@geeksquad.com>
*
* Copyright:
* (c) 2012 Geek Squad. All rights reserved.
*/
var CandyShop = (function(self) { return self; }(CandyShop || {}));
/** Class: CandyShop.StickySubject
* This plugin makes it so the room subject is always visible
*/
CandyShop.StickySubject = (function(self, Candy, $) {
/** Function: init
* Initialize the StickySubject plugin
*/
self.init = function() {
// Listen for a subject change in the room
$(Candy).on('candy:view.room.after-subject-change', function(e, data) {
// get the current message pane and create the text
var $messagePane = $(Candy.View.Pane.Room.getPane(Candy.View.getCurrent().roomJid)),
subjectText = $.i18n._('roomSubject') + ' ' + data.subject;
// if we don't have the subject container yet, add it
// else just update the content
if ($('.candy-subject-container:visible').length == 0) {
$messagePane.prepend('<div class="candy-subject-container">' + subjectText + '</div>');
$messagePane.find('.message-pane-wrapper').addClass('candy-has-subject');
} else {
$messagePane.find('.candy-subject-container').html(subjectText);
}
});
};
return self;
}(CandyShop.StickySubject || {}, Candy, jQuery));
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
.message-pane dt { .message-pane li abbr {
width: 120px;
padding-right: 10px;
}
.message-pane dt abbr {
border-bottom: none; border-bottom: none;
} }
\ No newline at end of file
...@@ -12,10 +12,10 @@ CandyShop.Timeago = (function(self, Candy, $) { ...@@ -12,10 +12,10 @@ CandyShop.Timeago = (function(self, Candy, $) {
self.init = function() { self.init = function() {
Candy.View.Template.Chat['adminMessage'] = '<dt><abbr title="{{time}}">{{time}}</abbr></dt><dd class="adminmessage"><span class="label">{{sender}}</span>{{subject}} {{message}}</dd>'; Candy.View.Template.Chat['adminMessage'] = '<li><small><abbr title="{{time}}">{{time}}</abbr></small><div class="adminmessage"><span class="label">{{sender}}</span><span class="spacer">▸</span>{{subject}} {{message}}</div></li>';
Candy.View.Template.Chat['infoMessage'] = '<dt><abbr title="{{time}}">{{time}}</abbr></dt><dd class="infomessage">{{subject}} {{message}}</dd>'; Candy.View.Template.Chat['infoMessage'] = '<li><small><abbr title="{{time}}">{{time}}</abbr></small><div class="infomessage"><span class="spacer">•</span>{{subject}} {{message}}</div></li>';
Candy.View.Template.Room['subject'] = '<dt><abbr title="{{time}}">{{time}}</abbr></dt><dd class="subject"><span class="label">{{roomName}}</span>{{_roomSubject}} {{subject}}</dd>'; Candy.View.Template.Room['subject'] = '<li><small><abbr title="{{time}}">{{time}}</abbr></small><div class="subject"><span class="label">{{roomName}}</span><span class="spacer">▸</span>{{_roomSubject}} {{subject}}</div></li>';
Candy.View.Template.Message['item'] = '<dt><abbr title="{{time}}">{{time}}</abbr></dt><dd><span class="label"><a href="#" class="name">{{displayName}}</a></span>{{{message}}}</dd>'; Candy.View.Template.Message['item'] = '<li><small><abbr title="{{time}}">{{time}}</abbr></small><div><a class="label" href="#" class="name">{{displayName}}</a><span class="spacer">▸</span>{{{message}}}</div></li>';
Candy.Util.localizedTime = function(dateTime) { Candy.Util.localizedTime = function(dateTime) {
if (dateTime === undefined) { if (dateTime === undefined) {
...@@ -26,21 +26,16 @@ CandyShop.Timeago = (function(self, Candy, $) { ...@@ -26,21 +26,16 @@ CandyShop.Timeago = (function(self, Candy, $) {
return date.format($.i18n._('isoDateTime')); return date.format($.i18n._('isoDateTime'));
}; };
Candy.View.Event.Message.onShow = function(message) { var applyTimeago = function(e, args) {
$('abbr').timeago(); var $elem = args.element ? $('abbr', args.element) : $('abbr');
$elem.timeago();
}; };
Candy.View.Event.Chat.onAdminMessage = function(message) { $(Candy).on('candy:view.message.after-show', applyTimeago);
$('abbr').timeago(); $(Candy).on('candy:view.room.after-subject-change', applyTimeago);
}; // the following handlers run timeago() on all <abbr> tags
$(Candy).on('candy:core.presence.room', applyTimeago);
Candy.View.Event.Room.onSubjectChange = function(message) { $(Candy).on('candy:view.chat.admin-message', applyTimeago);
$('abbr').timeago();
};
Candy.View.Event.Room.onPresenceChange = function(message) {
$('abbr').timeago();
};
}; };
return self; return self;
......
...@@ -13,14 +13,39 @@ ...@@ -13,14 +13,39 @@
<script type="text/javascript" src="candy.fix.js"></script> <script type="text/javascript" src="candy.fix.js"></script>
<script type="text/javascript" src="base64.js"></script> <script type="text/javascript" src="base64.js"></script>
<!-- available_rooms插件不能用 看不到任何效果 -->
<script type="text/javascript" src="candy-plugins/chatrecall/candy.js"></script>
<script type="text/javascript" src="candy-plugins/clearchat/candy.js"></script>
<link rel="stylesheet" type="text/css" href="candy-plugins/clearchat/candy.css" />
<!-- colors 没有i18n并且 由于不是标准颜色协议,会导致pidgen等三方客户端上直接显示代码,不启用-->
<script type="text/javascript" src="candy-plugins/inline-images/candy.js"></script> <script type="text/javascript" src="candy-plugins/inline-images/candy.js"></script>
<link rel="stylesheet" type="text/css" href="candy-plugins/inline-images/candy.css" /> <link rel="stylesheet" type="text/css" href="candy-plugins/inline-images/candy.css" />
<script type="text/javascript" src="candy-plugins/gs-notifyme/candy.js"></script> <!-- inline-videos插件不能用 看不到任何效果 -->
<link rel="stylesheet" type="text/css" href="candy-plugins/gs-notifyme/candy.css" /> <!-- join插件不能用 看不到任何效果 -->
<!-- jquery-ui 不需要 -->
<script type="text/javascript" src="candy-plugins/namecomplete/candy.js"></script>
<link rel="stylesheet" type="text/css" href="candy-plugins/namecomplete/candy.css" />
<script type="text/javascript" src="candy-plugins/gs-namecomplete/candy.js"></script> <!-- nickchange 没有i18n并且 不需要 -->
<link rel="stylesheet" type="text/css" href="candy-plugins/gs-namecomplete/candy.css" />
<!--node-webkit需要调教-->
<!--<script type="text/javascript" src="candy-plugins/notifications/candy.js"></script>-->
<script type="text/javascript" src="candy-plugins/notifyme/candy.js"></script>
<link rel="stylesheet" type="text/css" href="candy-plugins/notifyme/candy.css" />
<!-- refocus 不需要(?) -->
<!-- removeignore 不需要 -->
<!-- replies 目测跟notifyme一样 -->
<!-- roompanel 没有i18n 并且 有bug -->
<!-- stickysubject 不需要 -->
<!-- timeago 没有i18n -->
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function () { $(document).ready(function () {
...@@ -34,9 +59,12 @@ $(document).ready(function () { ...@@ -34,9 +59,12 @@ $(document).ready(function () {
view: { resources: 'res/', language: 'cn' } view: { resources: 'res/', language: 'cn' }
}); });
CandyShop.ChatRecall.init();
CandyShop.ClearChat.init(false);
CandyShop.InlineImages.init(); CandyShop.InlineImages.init();
CandyShop.NotifyMe.init();
CandyShop.NameComplete.init(); CandyShop.NameComplete.init();
<!--CandyShop.Notifications.init();-->
CandyShop.NotifyMe.init();
Candy.Core.connect(url.param('jid'), url.param('password')); Candy.Core.connect(url.param('jid'), url.param('password'));
......
...@@ -117,10 +117,13 @@ ...@@ -117,10 +117,13 @@
<label class="checkbox"> <label class="checkbox">
<input type="checkbox" value="remember-me" ng-model="user.remember_me"> 记住密码 <input type="checkbox" value="remember-me" ng-model="user.remember_me"> 记住密码
</label> </label>
<button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="sign_in(user)" ng-disabled="signing">登录</button> <button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="sign_in(user)" ng-disabled="signing">
登录
</button>
</form> </form>
</div> <!-- /container --> </div>
<!-- /container -->
<div id="footer"> <div id="footer">
<div class="footer content clearfix"> <div class="footer content clearfix">
<ul id="footer-list"> <ul id="footer-list">
......
[ [
{"id": 1, "name": "杭州", "url": "ws://115.29.191.63:10800/"}, {"id": 1, "name": "杭州", "url": "ws://115.29.191.63:10800/"},
{"id": 2, "name": "北京", "url": "ws://122.0.65.69:10800/"} {"id": 2, "name": "北京", "url": "ws://117.121.26.153:10800/"}
] ]
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment