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

candy-plugins

parent bb7407a5
......@@ -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.
## 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.
* __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
* __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.
* __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".
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).
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, $) {
});
}
$(Candy.View.Pane).on('candy:view.message.before-send', function(e, args) {
$(Candy).on('candy:view.message.before-send', function(e, args) {
// (strip colors)
// if it matches '/clear', clear the chat window and don't send anything
if (args.message.replace(/\|c:\d+\|/, '').toLowerCase() == '/clear') {
......
......@@ -10,14 +10,14 @@ CandyShop.Colors = (function(self, Candy, $) {
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) !== '') {
args.message = '|c:'+ _currentColor +'|' + args.message;
}
});
$(Candy.View.Pane).on('candy:view.message.before-show', function(e, args) {
args.message = args.message.replace(/^\|c:([0-9]{1,2})\|(.*)/gm, '<span class="colored color-$1">$2</span>');
$(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')) {
......@@ -65,13 +65,21 @@ CandyShop.Colors = (function(self, Candy, $) {
};
self.applyTranslations = function() {
Candy.View.Translation.en.candyshopColorsMessagecolor = 'Message color';
Candy.View.Translation.ru.candyshopColorsMessagecolor = 'Цвет сообщения';
Candy.View.Translation.de.candyshopColorsMessagecolor = 'Farbe für Nachrichten';
Candy.View.Translation.fr.candyshopColorsMessagecolor = 'Couleur des messages';
Candy.View.Translation.nl.candyshopColorsMessagecolor = 'Berichtkleur';
Candy.View.Translation.es.candyshopColorsMessagecolor = 'Color de los mensajes';
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));
\ No newline at end of file
}(CandyShop.Colors || {}, Candy, jQuery));
......@@ -2,6 +2,7 @@
* inline-images
* @version 1.0
* @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
* inside of Candy.
......@@ -12,16 +13,14 @@ var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.InlineImages = (function(self, Candy, $) {
var _fileExtensions = ['png','jpg','jpeg','gif']
,_originalLinkify = Candy.Util.Parser.linkify
,_maxImageSize = 100;
/** Function: init
* Initializes the inline-images plugin with the default settings.
*/
self.init = function() {
$(Candy.View.Pane).on('candy:view.message.before-show', handleBeforeShow);
$(Candy.View.Pane).on('candy:view.message.after-show', handleOnShow);
Candy.Util.Parser.linkify = linkify;
$(Candy).on('candy:view.message.before-show', handleBeforeShow);
$(Candy).on('candy:view.message.after-show', handleOnShow);
};
/** Function: initWithFileExtensions
......@@ -33,7 +32,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
*/
self.initWithFileExtensions = function(fileExtensions) {
_fileExtensions = fileExtensions;
init();
self.init();
};
/** Function: initWithMaxImageSize
......@@ -45,7 +44,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
*/
self.initWithMaxImageSize = function(maxImageSize) {
_maxImageSize = maxImageSize;
init();
self.init();
};
/** Function: initWithFileExtensionsAndMaxImageSize
......@@ -60,7 +59,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
self.initWithFileExtensionsAndMaxImageSize = function(fileExtensions, maxImageSize) {
_fileExtensions = fileExtensions;
_maxImageSize = maxImageSize;
init();
self.init();
};
......@@ -75,9 +74,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
*/
var handleBeforeShow = function(e, args) {
var message = args.message;
var processed = message.replace(/\|[^\|]+\|/, "");
processed = processed.replace(/(^|[^\/])(www|i\.[^\.]+\.[\S]+(\b|$))/gi, '$1http://$2');
processed = processed.replace(/\b(https?:\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, replaceCallback);
var processed = message.replace(/>(\b(https?|ftp|file):\/\/[\-A-Z0-9+&@#\/%?=~_|!:,.;]*[\-A-Z0-9+&@#\/%=~_|])</ig, replaceCallback);
args.message = processed;
return processed;
};
......@@ -113,22 +110,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
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
* This callback handles matches from the URL regex.
......@@ -136,25 +118,23 @@ CandyShop.InlineImages = (function(self, Candy, $) {
* indicator. If it is just a common URL, a link-tag gets returned.
*
* Paramters:
* (String) uselessMatch - whole url with ">" and "<"
* (String) match - matched URL
*
* Returns:
* (String)
*/
var replaceCallback = function(match) {
var replaceCallback = function(uselessMatch, match) {
var result = match;
var dotPosition = match.lastIndexOf(".");
if(dotPosition > -1) {
if(_fileExtensions.indexOf(match.substr(dotPosition+1)) != -1) {
result = buildImageLoaderSource(match);
} else {
result = buildLinkSource(match);
}
}
return result;
}
return '>' + result + '<';
};
/** Function: buildImageLoaderSource
* Returns a loader indicator. The handleOnShow method fullfills afterwards
......@@ -167,8 +147,8 @@ CandyShop.InlineImages = (function(self, Candy, $) {
* (String)
*/
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
* Returns HTML source to show a URL as an image.
......@@ -181,20 +161,7 @@ CandyShop.InlineImages = (function(self, Candy, $) {
*/
var buildImageSource = function(url, width, height) {
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;
}(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 @@
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/gs-namecomplete/candy.js"></script>
<link rel="stylesheet" type="text/css" href="path_to_plugins/gs-namecomplete/candy.css" />
<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)
`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
......
......@@ -2,6 +2,6 @@
background-color: #ccc;
}
#context-menu li.gs-namecomplete-option {
#context-menu li.candy-namecomplete-option {
padding: 3px 5px;
}
\ No newline at end of file
......@@ -29,6 +29,11 @@ CandyShop.NameComplete = (function(self, Candy, $) {
*/
var _nicks = [];
/** String: _selector
* The selector for the visible message box
*/
var _selector = 'input[name="message"]:visible';
/** Function: init
* Initialize the NameComplete plugin
* Show options for auto completion of names
......@@ -41,7 +46,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
$.extend(true, _options, options);
// 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 (e.which == _options.completeKeyCode) {
// update the list of nicks to grab
......@@ -121,7 +126,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
}
// remove the listener on the field
$('input[name="message"]').unbind('keydown', self.keyDown);
$(_selector).unbind('keydown', self.keyDown);
// hide the menu
menu.hide();
......@@ -142,7 +147,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
self.replaceName($(e.currentTarget).text());
$(document).unbind('keydown', self.keyDown);
$('#context-menu').hide();
$('input[name="message"]').focus();
$(_selector).focus();
e.preventDefault();
};
......@@ -154,12 +159,15 @@ CandyShop.NameComplete = (function(self, Candy, $) {
_nicks = [];
// grab the roster in the current room
var roster = Candy.Core.getRoom(Candy.View.getCurrent().roomJid).getRoster().getAll();
// iterate and add the nicks to the collection
$.each(roster, function(index, item) {
_nicks.push(_options.nameIdentifier + item.getNick());
});
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
......@@ -167,7 +175,8 @@ CandyShop.NameComplete = (function(self, Candy, $) {
*/
self.replaceName = function(replaceText) {
// 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 (msgParts.length==1) {
......@@ -180,7 +189,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
msgParts[msgParts.length - 1] = replaceText;
// put the string back together on spaces
$('input[name="message"]').val(msgParts.join(' '));
$msgBox.val(msgParts.join(' '));
}
/** Function: showPicker
......@@ -201,7 +210,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
// add the matches to the list
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
......@@ -210,7 +219,7 @@ CandyShop.NameComplete = (function(self, Candy, $) {
content.find('li').click(self.selectOnClick);
// 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?
// get the top of the box to put this thing at
......@@ -225,4 +234,4 @@ CandyShop.NameComplete = (function(self, Candy, $) {
};
return self;
}(CandyShop.NameComplete || {}, Candy, jQuery));
}(CandyShop.NameComplete || {}, Candy, jQuery));
\ No newline at end of file
# 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;
}
\ No newline at end of file
......@@ -39,18 +39,18 @@ CandyShop.NotifyMe = (function(self, Candy, $) {
$.extend(true, _options, options);
// 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
var nick = Candy.Core.getUser().getNick();
// 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 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
// 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
if (_options.playSound) {
Candy.View.Pane.Chat.Toolbar.playSound();
......@@ -58,7 +58,7 @@ CandyShop.NotifyMe = (function(self, Candy, $) {
// highlight if specified
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 {
height: 13px;
}
.message-pane dd.mention, .message-pane dt.mention {
.message-pane li.mention,
.message-pane li.mention small {
background-color: #FFF7DE;
}
\ No newline at end of file
/*
* candy-replies-plugin
* @version 0.1 (2013-2-20)
* @version 0.2 (2014-01-05)
* @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
* conversations.
*/
......@@ -12,24 +12,23 @@ var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.Replies = (function(self, Candy, $) {
self.init = function() {
Candy.View.Event.Message.onShow = handleOnShow;
$(Candy).on('candy:view.message.after-show', handleOnShow);
return self;
};
var handleOnShow = function(args) {
var handleOnShow = function(e, args) {
var localNick = Candy.Core.getUser().getNick();
var re = new RegExp("@" + localNick + "([ .!><\":\/@-]|$)", 'im');
if(re.test(args.message)) {
var el = args.element;
el.addClass("mention");
el.prev().addClass("mention");
}
}
return self;
}(CandyShop.Replies || {}, Candy, jQuery));
# RoomPanel
# RoomPanel
Adds Icon show a lists rooms, also allows to show rooms upon connection and when all rooms are closed.
![RoomPanel](screenshot.png)
## Usage
To enable *RoomPanel* you have to include its JavaScript code and stylesheet:
To enable *RoomPanel* you have to include its JavaScript code and stylesheet:
```HTML
<script type="text/javascript" src="candyshop/roomPanel/roomPanel.js"></script>
<link rel="stylesheet" type="text/css" href="candyshop/roomPanel/default.css" />
```
Call its `init()` method after Candy has been initialized:
Call its `init()` method after Candy has been initialized:
```JavaScript
Candy.init('/http-bind/');
Candy.init('/http-bind/', {core: {autojoin: []}});
// enable RoomPanel plugin
CandyShop.RoomPanel.init({
// 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: [
{
name: 'my room',
jid: 'my-room@conference.yourdomain.com'
jid: 'my-room@conference.example.com'
},
{
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]
showIfAllTabClosed: true,
// detect rooms before showing list, default value is true. [optional]
// 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]
autoDetectRooms: true,
// how long in seconds before refreshing room list, default value is 600. [optional]
roomCacheTime: 600
});
});
Candy.Core.connect();
```
.roomList a {
color: #AAA;
color: #333;
}
#roomPanel-control {
width: 16px;
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,46 +18,65 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
autoDetectRooms: true,
// 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
};
var _lastRoomUpdate = 0;
self.init = function(options) {
$.extend(_options, options);
self.applyTranslations();
/* Overwrite candy allTabsClosed function not
* to disconnect when all tags are closed */
* to disconnect when all tabs are closed */
if (_options.showIfAllTabClosed) {
Candy.View.Pane.Chat.allTabsClosed = function () {
CandyShop.RoomPanel.showRoomPanel();
self.showRoomPanel();
return;
};
} //if
var html = '<li id="roomPanel-control" data-tooltip="' + $.i18n._('candyshopRoomPanelListRoom') + '"></li>';
$('#chat-toolbar').prepend(html);
$('#roomPanel-control').click(function() {
CandyShop.RoomPanel.showRoomPanel();
});
if(_options.showToolbarIcon !== false) {
var html = '<li id="roomPanel-control" data-tooltip="' + $.i18n._('candyshopRoomPanelListRoom') + '"></li>';
$('#chat-toolbar').prepend(html);
$('#roomPanel-control').click(function() {
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) {
if (data.type == 'connection') {
if (Strophe.Status.CONNECTED == data.status) {
/* 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);
} //if
$(Candy).on('candy:core.chat.connection', function(obj, data) {
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 */
setTimeout(CandyShop.RoomPanel.showRoomPanelIfAllClosed, 500);
} //if
return true;
}});
});
};
self.showRoomPanelIfAllClosed = function() {
var roomCount = 0;
var rooms = Candy.Core.getRooms();
for (k in rooms) {
......@@ -67,30 +86,30 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
} //for
if (roomCount == 0) {
CandyShop.RoomPanel.showRoomPanel();
self.showRoomPanel();
} //if
}
self.updateRoomList = function (iq) {
var newRoomList = [];
$('item', iq).each(function (index, value) {
var name = $(value).attr('name');
var jid = $(value).attr('jid');
if (typeof name == 'undefined') {
name = jid.split('@')[0];
} //if
newRoomList.push({
name: name,
jid: jid
name: Strophe.unescapeNode(name),
jid: Candy.Util.unescapeJid(jid)
});
});
_options.roomList = newRoomList;
_lastRoomUpdate = Math.round(new Date().getTime() / 1000);
self.showRoomPanel();
};
......@@ -103,9 +122,9 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
var timeDiff = Math.round(new Date().getTime() / 1000) - _options.roomCacheTime;
if (_options.autoDetectRooms && timeDiff > _lastRoomUpdate ) {
/* 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});
Candy.Core.getConnection().sendIQ(iq, self.updateRoomList);
} else {
......@@ -121,30 +140,29 @@ CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
Candy.View.Pane.Chat.Modal.hide();
e.preventDefault();
});
} //if
} //if
return true;
};
self.applyTranslations = function() {
Candy.View.Translation.en.candyshopRoomPanelListRoom = 'List Rooms';
Candy.View.Translation.ru.candyshopRoomPanelListRoom = 'Список комнат';
Candy.View.Translation.de.candyshopRoomPanelListRoom = 'Verfügbare Räume anzeigen';
Candy.View.Translation.fr.candyshopRoomPanelListRoom = 'Liste des salles';
Candy.View.Translation.nl.candyshopRoomPanelListRoom = 'List Rooms';
Candy.View.Translation.es.candyshopRoomPanelListRoom = 'List Rooms';
Candy.View.Translation.en.candyshopRoomPanelChooseRoom = 'Choose Room To Join';
Candy.View.Translation.ru.candyshopRoomPanelChooseRoom = 'Выберите комнату ';
Candy.View.Translation.de.candyshopRoomPanelChooseRoom = 'Verfügbare Räume';
Candy.View.Translation.fr.candyshopRoomPanelChooseRoom = 'Choisir une salle';
Candy.View.Translation.nl.candyshopRoomPanelChooseRoom = 'Choose Room To Join';
Candy.View.Translation.es.candyshopRoomPanelChooseRoom = 'Choose Room To Join';
var translations = {
'en' : ['List Rooms', 'Choose Room To Join'],
'ru' : ['Список комнат', 'Выберите комнату'],
'de' : ['Verfügbare Räume anzeigen', 'Verfügbare Räume'],
'fr' : ['Choisir une salle', 'Liste des salles'],
'nl' : ['Choose Room To Join', 'List Rooms'],
'es' : ['Choose Room To Join', 'List Rooms']
};
$.each(translations, function(k, v) {
if(Candy.View.Translation[k]) {
Candy.View.Translation[k].candyshopRoomPanelListRoom = v[0];
Candy.View.Translation[k].candyshopRoomPanelChooseRoom = v[1];
}
});
};
return self;
......@@ -161,8 +179,8 @@ CandyShop.RoomPanel.Template = (function (self) {
'</ul>',
'</div>'
];
self.rooms = roomParts.join('');
return self;
})(CandyShop.RoomPanel.Template || {});
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 {
width: 120px;
padding-right: 10px;
}
.message-pane dt abbr {
.message-pane li abbr {
border-bottom: none;
}
\ No newline at end of file
......@@ -11,36 +11,31 @@ var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.Timeago = (function(self, Candy, $) {
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['infoMessage'] = '<dt><abbr title="{{time}}">{{time}}</abbr></dt><dd class="infomessage">{{subject}} {{message}}</dd>';
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.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.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'] = '<li><small><abbr title="{{time}}">{{time}}</abbr></small><div class="infomessage"><span class="spacer">•</span>{{subject}} {{message}}</div></li>';
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'] = '<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) {
if (dateTime === undefined) {
return undefined;
}
var date = Candy.Util.iso8601toDate(dateTime);
return date.format($.i18n._('isoDateTime'));
};
Candy.View.Event.Message.onShow = function(message) {
$('abbr').timeago();
};
Candy.View.Event.Chat.onAdminMessage = function(message) {
$('abbr').timeago();
};
Candy.View.Event.Room.onSubjectChange = function(message) {
$('abbr').timeago();
};
Candy.View.Event.Room.onPresenceChange = function(message) {
$('abbr').timeago();
};
var applyTimeago = function(e, args) {
var $elem = args.element ? $('abbr', args.element) : $('abbr');
$elem.timeago();
};
$(Candy).on('candy:view.message.after-show', applyTimeago);
$(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).on('candy:view.chat.admin-message', applyTimeago);
};
return self;
......
......@@ -13,14 +13,39 @@
<script type="text/javascript" src="candy.fix.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>
<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>
<link rel="stylesheet" type="text/css" href="candy-plugins/gs-notifyme/candy.css" />
<!-- inline-videos插件不能用 看不到任何效果 -->
<!-- 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>
<link rel="stylesheet" type="text/css" href="candy-plugins/gs-namecomplete/candy.css" />
<!-- nickchange 没有i18n并且 不需要 -->
<!--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">
$(document).ready(function () {
......@@ -34,9 +59,12 @@ $(document).ready(function () {
view: { resources: 'res/', language: 'cn' }
});
CandyShop.ChatRecall.init();
CandyShop.ClearChat.init(false);
CandyShop.InlineImages.init();
CandyShop.NotifyMe.init();
CandyShop.NameComplete.init();
<!--CandyShop.Notifications.init();-->
CandyShop.NotifyMe.init();
Candy.Core.connect(url.param('jid'), url.param('password'));
......
......@@ -117,38 +117,41 @@
<label class="checkbox">
<input type="checkbox" value="remember-me" ng-model="user.remember_me"> 记住密码
</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>
</div> <!-- /container -->
<div id="footer">
<div class="footer content clearfix">
<ul id="footer-list">
<li>
毛玉
</li>
<li>
<a href="javascript:;">
隐私权和服务条款
</a>
</li>
<li>
<a href="javascript:;">
帮助
</a>
</li>
</ul>
<div id="lang-vis-control" style="display: inline;">
<span id="lang-chooser-wrap" class="lang-chooser-wrap">
<label for="lang-chooser"><img src="https://ssl.gstatic.com/images/icons/ui/common/universal_language_settings-21.png" alt="更改语言"></label>
<select id="lang-chooser" class="lang-chooser" name="lang-chooser">
<option value="zh-CN" selected="selected">
‪简体中文‬
</option>
</select>
</span>
</div>
</div>
<!-- /container -->
<div id="footer">
<div class="footer content clearfix">
<ul id="footer-list">
<li>
毛玉
</li>
<li>
<a href="javascript:;">
隐私权和服务条款
</a>
</li>
<li>
<a href="javascript:;">
帮助
</a>
</li>
</ul>
<div id="lang-vis-control" style="display: inline;">
<span id="lang-chooser-wrap" class="lang-chooser-wrap">
<label for="lang-chooser"><img src="https://ssl.gstatic.com/images/icons/ui/common/universal_language_settings-21.png" alt="更改语言"></label>
<select id="lang-chooser" class="lang-chooser" name="lang-chooser">
<option value="zh-CN" selected="selected">
‪简体中文‬
</option>
</select>
</span>
</div>
</div>
</div>
</div>
......
[
{"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