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

init

parents
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
logs/*
!.gitkeep
node_modules/
tmp
.DS_Store
.idea
cache
apps
bin
*.exe
*.bat
This diff is collapsed.
# 毛玉
这是一个正在开发中的ACG同人向游戏平台
毛玉只是开发代号,不作为最终名称,可能会并入mycard
## 功能列表 (发布之前)
用户登录 ( 除xmpp外还有个http api http://my-card.in/users/me.json?name=用户名&password=密码 )
聊天
自动更新
以下功能至少支持东方绯想天、非想天则、心绮楼
下载
基于服务器转发的联机 (反重力场) (已实现一份windows-only的。windows-only纯粹因为提权,其他平台可以用setuid之类的办法更容易实现,不用弹提权确认窗)
语言切换 (考虑一下语言包怎么定义,可能还会有其他MOD? DLC? 暂时想不到什么)
profile名称修改 (注意泛用性,抽个接口,before_run之类。还有FXTZ/FXT合体)
## 想到的功能列表 (有生之年)
首页
公告
推荐
捐款
游戏
自动匹配对战
好友约战
(对于有定制规则的游戏) 基于房间的自定义游戏 大厅
replay管理
排行榜
收集 (成就、符卡等)
游戏内个人信息(头像等?)
存档同步
自动更新
一定程度的跨平台,例如能wine起来的游戏就直接标记为支持linux、mac。下载的时候一起调教好。
依赖 (黑历史的PC98模拟器之类,还有FXTZ->FXT的建议性依赖)
游戏和其他资源
好友玩过/好友正在玩
评论
评分
社交分享(需要么)
(漫画)弹幕(需要么)
删除
## 资源打包格式
如果不是原作者亲自发布的,要注意官方发布的版本有哪些文件就只带那些,不要夹带私活
对于给用户看的文本文件,以UTF-8无BOM编码来保存
文件压缩为7z格式,不要封一层目录,散着放进来。压缩选项文件列表UTF-8编码 ( -scsUTF-8 ), 其余默认
对于语言包,如果翻译了原本的文本文件,直接原始文件名(history.txt , 而不是history(zh).txt)。而资源和可执行文件按翻译组的意见(th123_beta.exe)
## 目录结构
app/ --> 代码和资源
css/ --> 样式表
img/ --> 图片
index.html --> 页面
js/ --> javascript files
app.js --> application
controllers.js --> angular controllers 主要逻辑在这里
directives.js --> angular directives 还不会玩
filters.js --> angular filters 在模板里调用的函数
services.js --> angular services 还不会玩
lib/ --> 三方库
partials/ --> angular view partials 模板
app_show.html --> 游戏的模板
bin/ --> 二进制文件,平台相关,不入git
7za.exe --> http://7-zip.org/download.html zip
aria2c.exe --> http://aria2.sourceforge.net/
node.exe --> http://nodejs.org/ UAC提权时候用
nw.exe --> https://github.com/rogerwang/node-webkit
maotama.exe --> 给用户调用的主程序,winrar自解压 静默 "bin\nw.exe" .
LICENSE --> 许可证 AGPL
README.md --> 本文档
apps.json --> 调试用数据库,生产时应当在服务器上
loop_start.bat --> 调试用循环启动,叉掉之后自动开启一个新实例
## 相关技术
nodejs
node-webkit
HTML5
AngularJS
## 制作组
+神楽坂玲奈 <zh99998@gmail.com>
+艾可 <arzusyume@gmail.com>
# Candy Plugins
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
* __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.
* __jQuery-Ui__ - jQuery UI lightness theme
* __Room Panel__ - Provides a list of rooms available to join.
* __Timeago__ - Replaces the exact time/date with fuzzy timestamps like "2 minutes ago".
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.
# Colors
Send and receive colored messages.
![Color Picker](screenshot.png)
## Usage
To enable *Colors* you have to include its JavaScript code and stylesheet:
```HTML
<script type="text/javascript" src="candyshop/colors/candy.js"></script>
<link rel="stylesheet" type="text/css" href="candyshop/colors/candy.css" />
```
Call its `init()` method after Candy has been initialized:
```JavaScript
Candy.init('/http-bind/');
// enable Colors plugin (default: 8 colors)
CandyShop.Colors.init();
Candy.Core.connect();
```
To enable less or more colors just call `CandyShop.Colors.init(<number-of-colors>)`.
#colors-control {
background: no-repeat url('colors-control.png');
position: relative;
}
#colors-control-indicator {
display: inline-block;
height: 6px;
width: 6px;
border: 1px solid white;
position: absolute;
top: 100%;
left: 100%;
margin: -8px 0 0 -8px;
}
#context-menu .colors {
padding-left: 5px;
width: 89px;
white-space: normal;
}
#context-menu .colors:hover {
background-color: inherit;
}
#context-menu .colors span {
display: inline-block;
width: 14px;
height: 14px;
border: 1px solid white;
margin: 3px;
}
.message-pane span.colored {
background-color: transparent !important;
}
.color-0 {
color: #333;
background-color: #333;
}
.color-1 {
color: #c4322b;
background-color: #c4322b;
}
.color-2 {
color: #37991e;
background-color: #37991e;
}
.color-3 {
color: #1654c9;
background-color: #1654c9;
}
.color-4 {
color: #66379b;
background-color: #66379b;
}
.color-5 {
color: #ba7318;
background-color: #ba7318;
}
.color-6 {
color: #32938a;
background-color: #32938a;
}
.color-7 {
color: #9e2274;
background-color: #9e2274;
}
.color-8 {
color: #4C82E4;
background-color: #4C82E4;
}
.color-9 {
color: #7F140E;
background-color: #7F140E;
}
.color-10 {
color: #1C630A;
background-color: #1C630A;
}
.color-11 {
color: #CF55A4;
background-color: #CF55A4;
}
\ No newline at end of file
var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.Colors = (function(self, Candy, $) {
var _numColors,
_currentColor = 0;
self.init = function(numColors) {
_numColors = numColors ? numColors : 8;
self.applyTranslations();
$(Candy.View.Pane).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>');
});
if(Candy.Util.cookieExists('candyshop-colors-current')) {
var color = parseInt(Candy.Util.getCookie('candyshop-colors-current'), 10);
if(color > 0 && color < _numColors) {
_currentColor = color;
}
}
var html = '<li id="colors-control" data-tooltip="' + $.i18n._('candyshopColorsMessagecolor') + '"><span class="color-' + _currentColor + '" id="colors-control-indicator"></span></li>';
$('#emoticons-icon').after(html);
$('#colors-control').click(function(event) {
CandyShop.Colors.showPicker(this);
});
};
self.showPicker = function(elem) {
elem = $(elem);
var pos = elem.offset(),
menu = $('#context-menu'),
content = $('ul', menu),
colors = '',
i;
$('#tooltip').hide();
for(i = _numColors-1; i >= 0; i--) {
colors = '<span class="color-' + i + '" data-color="' + i + '"></span>' + colors;
}
content.html('<li class="colors">' + colors + '</li>');
content.find('span').click(function() {
_currentColor = $(this).attr('data-color');
$('#colors-control-indicator').attr('class', 'color-' + _currentColor);
Candy.Util.setCookie('candyshop-colors-current', _currentColor, 365);
Candy.View.Pane.Room.setFocusToForm(Candy.View.getCurrent().roomJid);
menu.hide();
});
var posLeft = Candy.Util.getPosLeftAccordingToWindowBounds(menu, pos.left),
posTop = Candy.Util.getPosTopAccordingToWindowBounds(menu, pos.top);
menu.css({'left': posLeft.px, 'top': posTop.px, backgroundPosition: posLeft.backgroundPositionAlignment + ' ' + posTop.backgroundPositionAlignment});
menu.fadeIn('fast');
return true;
};
self.applyTranslations = function() {
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';
};
return self;
}(CandyShop.Colors || {}, Candy, jQuery));
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
#clearchat-control {
background: transparent url('page_refresh.png') no-repeat;
position: relative;
}
\ 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.ClearChat
* Clears the chat window if either clicked or '/clear' is typed
*/
CandyShop.ClearChat = (function(self, Candy, $) {
/** Function: init
* Initialize the ClearChat plugin
* Bind event and if specified, add the icon to be clickable
*
* Parameters:
* (Boolean) showInToolbar - Whether to add it to the toolbar
*/
self.init = function(showInToolbar) {
// add the translations
self.applyTranslations();
// if it's specified to show the control,
// add it to the toolbar
if (showInToolbar) {
var html = '<li id="clearchat-control" data-tooltip="' + $.i18n._('candyshopClearchat') + '"></li>';
$('#emoticons-icon').after(html);
$('#clearchat-control').click(function() {
self.clearCurrentTab();
});
}
$(Candy.View.Pane).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') {
self.clearCurrentTab();
args.message = '';
}
});
}
/** Function: clearCurrentTab
* Clear the current tab's content
*/
self.clearCurrentTab = function() {
try {
// find the visible room, and empty the panel
$('.room-pane').filter(':visible').find('.message-pane').empty();
} catch (e) {
}
}
/** Function: applyTranslations
* Apply translations to this plugin
*/
self.applyTranslations = function() {
Candy.View.Translation.en.candyshopClearchat = 'Clear chat';
};
return self;
}(CandyShop.ClearChat || {}, Candy, jQuery));
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
# Name completion plugin
This plugin will complete the names of users in the room when a specified key is pressed.
### Usage
<script type="text/javascript" src="path_to_plugins/gs-namecomplete/candy.js"></script>
<link rel="stylesheet" type="text/css" href="path_to_plugins/gs-namecomplete/candy.css" />
...
CandyShop.NameComplete.init();
### Configuration options
nameIdentifier - String - The identifier to look for in a string. Defaults to '@'
completeKeyCode - Integer - The key code of the key to use. Defaults to 9 (tab)
### Example configurations
// complete the name when the user types +nick and hits the right arrow
// +troymcc -> +troymccabe
CandyShop.NameComplete.init({
nameIdentifier: '+',
completeKeyCode: '39'
});
// complete the name when the user types -nick and hits the up arrow
// +troymcc ^ +troymccabe
CandyShop.NameComplete.init({
nameIdentifier: '-',
completeKeyCode: '38'
});
\ No newline at end of file
#context-menu li.selected {
background-color: #ccc;
}
#context-menu li.gs-namecomplete-option {
padding: 3px 5px;
}
\ 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.NameComplete
* Allows for completion of a name in the roster
*/
CandyShop.NameComplete = (function(self, Candy, $) {
/** Object: _options
* Options:
* (String) nameIdentifier - Prefix to append to a name to look for. '@' now looks for '@NICK', '' looks for 'NICK', etc. Defaults to '@'
* (Integer) completeKeyCode - Which key to use to complete
*/
var _options = {
nameIdentifier: '@',
completeKeyCode: 9
};
/** Array: _nicks
* An array of nicks to complete from
* Populated after 'candy:core.presence'
*/
var _nicks = [];
/** Function: init
* Initialize the NameComplete plugin
* Show options for auto completion of names
*
* Parameters:
* (Object) options - Options to apply to this plugin
*/
self.init = function(options) {
// apply the supplied options to the defaults specified
$.extend(true, _options, options);
// listen for keydown when autocomplete options exist
$(document).on('keydown', 'input[name="message"]', function(e) {
// if we hear the key code for completion
if (e.which == _options.completeKeyCode) {
// update the list of nicks to grab
self.populateNicks();
// set up the vars for this method
// break it on spaces, and get the last word in the string
var field = $(this);
var msgParts = field.val().split(' ');
var lastWord = new RegExp( "^" + msgParts[msgParts.length - 1], "i");
var matches = [];
// go through each of the nicks and compare it
$(_nicks).each(function(index, item) {
// if we have results
if (item.match(lastWord) !== null) {
matches.push(item);
}
});
// if we only have one match, no need to show the picker, just replace it
// else show the picker of the name matches
if (matches.length == 1) {
self.replaceName(matches[0]);
} else if (matches.length > 1) {
self.showPicker(matches, field);
}
// don't perform any key actions
e.preventDefault();
}
});
}
/** Function: keyDown
* The listener for keydown in the menu
*/
self.keyDown = function(e) {
// get the menu and the content element
var menu = $('#context-menu'),
content = menu.find('ul'),
selected = content.find('li.selected');
if(menu.css('display') == 'none') {
$(document).unbind('keydown', self.keyDown);
return;
}
// switch the key code
switch (e.which) {
// up arrow
case 38:
// move the selected thing up
content.find('li.selected').removeClass('selected').prev().addClass('selected');
break;
// down arrow
case 40:
// move the selected thing down
content.find('li.selected').removeClass('selected').next().addClass('selected');
break;
// the key code for completion
case _options.completeKeyCode:
// esc key
case 27:
// delete Key
case 8:
case 46:
case 13:
if (e.which == _options.completeKeyCode || e.which == 13) {
// get the text of the selected item
var val = content.find('li.selected').text();
// replace the last item with the selected item
self.replaceName(val);
}
// remove the listener on the field
$('input[name="message"]').unbind('keydown', self.keyDown);
// hide the menu
menu.hide();
break;
}
// stop the action of any keys
e.preventDefault();
};
/** Function: selectOnClick
* The listener for click on decision in the menu
*
* Parameters:
* (Event) e - The click event
*/
self.selectOnClick = function(e) {
self.replaceName($(e.currentTarget).text());
$(document).unbind('keydown', self.keyDown);
$('#context-menu').hide();
$('input[name="message"]').focus();
e.preventDefault();
};
/** Function: populateNicks
* Populate the collection of nicks to autocomplete from
*/
self.populateNicks = function() {
// clear the nick collection
_nicks = [];
// grab the roster in the current room
var 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());
});
}
/** Function: replaceName
*
*/
self.replaceName = function(replaceText) {
// get the parts of the message
var msgParts = $('input[name="message"]').val().split(' ');
// If the name is the first word, add a colon to the end
if (msgParts.length==1) {
replaceText += ": "
} else {
replaceText += " "
}
// replace the last part with the item
msgParts[msgParts.length - 1] = replaceText;
// put the string back together on spaces
$('input[name="message"]').val(msgParts.join(' '));
}
/** Function: showPicker
* Show the picker for the list of names that match
*/
self.showPicker = function(matches, elem) {
// get the element
elem = $(elem);
// get the necessary items
var pos = elem.offset(),
menu = $('#context-menu'),
content = $('ul', menu),
i;
// clear the content if needed
content.empty();
// add the matches to the list
for(i = 0; i < matches.length; i++) {
content.append('<li class="gs-namecomplete-option">' + matches[i] + '</li>');
}
// select the first item
$(content.find('li')[0]).addClass('selected');
content.find('li').click(self.selectOnClick);
// bind the keydown to move around the menu
$('input[name="message"]').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
var posLeft = elem.val().length * 7,
posTop = Candy.Util.getPosTopAccordingToWindowBounds(menu, pos.top);
// show it
menu.css({'left': posLeft, 'top': posTop.px, backgroundPosition: posLeft.backgroundPositionAlignment + ' ' + posTop.backgroundPositionAlignment});
menu.fadeIn('fast');
return true;
};
return self;
}(CandyShop.NameComplete || {}, Candy, jQuery));
.gs-notifyme-highlight {
background: #FFFF00;
}
\ 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.NotifyMe
* Notifies with a sound and highlights the text in the chat when a nick is called out
*/
CandyShop.NotifyMe = (function(self, Candy, $) {
/** Object: _options
* Options for this plugin's operation
*
* Options:
* (String) nameIdentifier - Prefix to append to a name to look for. '@' now looks for '@NICK', '' looks for 'NICK', etc. Defaults to '@'
* (Boolean) playSound - Whether to play a sound when identified. Defaults to true
* (Boolean) highlightInRoom - Whether to highlight the name in the room. Defaults to true
*/
var _options = {
nameIdentifier: '@',
playSound: true,
highlightInRoom: true
};
/** Function: init
* Initialize the NotifyMe plugin
* Bind to beforeShow, play sound and higlight if specified
*
* Parameters:
* (Object) options - The options to apply to this plugin
*/
self.init = function(options) {
// apply the supplied options to the defaults specified
$.extend(true, _options, options);
// bind to the beforeShow event
$(Candy.View.Pane).on('candy:view.message.before-show', 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
var searchTerm = _options.nameIdentifier + nick;
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) {
// play the sound if specified
if (_options.playSound) {
Candy.View.Pane.Chat.Toolbar.playSound();
}
// highlight if specified
if (_options.highlightInRoom) {
args.message = args.message.replace(searchRegExp, '<span class="gs-notifyme-highlight">' + searchTerm + '</span>');
}
}
});
}
return self;
}(CandyShop.NotifyMe || {}, Candy, jQuery));
\ No newline at end of file
# Inline Images
If a user posts a URL to an image, that image gets rendered directly inside of Candy.
![Inline Images](screenshot.png)
## Usage
Include the JavaScript and CSS files:
```HTML
<script type="text/javascript" src="candyshop/inline-images/candy.js"></script>
<link rel="stylesheet" type="text/css" href="candyshop/inline-images/candy.css" />
```
To enable the Inline Images plugin, just add one of the ´init´ methods to your bootstrap:
```JavaScript
// init with default settings:
CandyShop.InlineImages.init();
// customized initialization:
CandyShop.InlineImages.initWithFileExtensions(['png','jpg']); // only recognize PNG and JPG files as image
CandyShop.InlineImages.initWithMaxImageSize(150); // resize images to a maximum edge size of 150px
CandyShop.InlineImages.initWithFileExtensionsAndMaxImageSize(['png','jpg'], 150); // combination of the above examples
```
\ No newline at end of file
.inlineimages-link {
text-decoration:none;
position:relative;
display:inline-block;
}
.inlineimages-link:hover:before {
content: url('overlay.png');
position: absolute;
top: 5px;
left: 5px;
opacity: .8;
}
\ No newline at end of file
/*
* inline-images
* @version 1.0
* @author Manuel Alabor (manuel@alabor.me)
*
* If a user posts a URL to an image, that image gets rendered directly
* inside of Candy.
*/
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;
};
/** Function: initWithFileExtensions
* Initializes the inline-images plugin with the possibility to pass an
* array with all the file extensions you want to display as image.
*
* Parameters:
* (String array) fileExtensions - Array with extensions (jpg, png, ...)
*/
self.initWithFileExtensions = function(fileExtensions) {
_fileExtensions = fileExtensions;
init();
};
/** Function: initWithMaxImageSize
* Initializes the inline-images plugin with the possibility to pass the
* maximum image size for displayed images.
*
* Parameters:
* (int) maxImageSize - Maximum edge size for images
*/
self.initWithMaxImageSize = function(maxImageSize) {
_maxImageSize = maxImageSize;
init();
};
/** Function: initWithFileExtensionsAndMaxImageSize
* Initializes the inline-images plugin with the possibility to pass an
* array with all the file extensions you want to display as image and
* the maximum image size for displayed images.
*
* Parameters:
* (String array) fileExtensions - Array with extensions (jpg, png, ...)
* (int) maxImageSize - Maximum edge size for images
*/
self.initWithFileExtensionsAndMaxImageSize = function(fileExtensions, maxImageSize) {
_fileExtensions = fileExtensions;
_maxImageSize = maxImageSize;
init();
};
/** Function: handleBeforeShow
* Handles the beforeShow event of a message.
*
* Paramteres:
* (Object) args - {roomJid, element, nick, message}
*
* Returns:
* (String)
*/
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);
args.message = processed;
return processed;
};
/** Function: handleOnShow
* Each time a message gets displayed, this method checks for possible
* image loaders (created by buildImageLoaderSource).
* If there is one, the image "behind" the loader gets loaded in the
* background. As soon as the image is loaded, the image loader gets
* replaced by proper scaled image.
*
* Parameters:
* (Array) args
*/
var handleOnShow = function(e, args) {
$('.inlineimages-loader').each(function(index, element) {
$(element).removeClass('inlineimages-loader');
var url = $(element).attr('longdesc');
var imageLoader = new Image();
$(imageLoader).load(function() {
var origWidth = this.width;
var origHeight = this.height;
if(origWidth > _maxImageSize || origHeight > _maxImageSize) {
var ratio = Math.min(_maxImageSize / origWidth, _maxImageSize / origHeight);
var width = Math.round(ratio * origWidth);
var height = Math.round(ratio * origHeight);
}
$(element).replaceWith(buildImageSource(url, width, height))
});
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.
* If the callback detects an image URL, it returns an image with a loading
* indicator. If it is just a common URL, a link-tag gets returned.
*
* Paramters:
* (String) match - matched URL
*
* Returns:
* (String)
*/
var replaceCallback = function(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;
}
/** Function: buildImageLoaderSource
* Returns a loader indicator. The handleOnShow method fullfills afterwards
* the effective image loading.
*
* Parameters:
* (String) url - image url
*
* Returns:
* (String)
*/
var buildImageLoaderSource = function(url) {
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.
*
* Parameters:
* (String) url - image url
*
* Returns:
* (String)
*/
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));
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
#Candy jQuery UI lightness Theme plugin
This plugin replaces the default theme with the jQuery UI lightness Theme. (http://jqueryui.com/)
##Usage
To enable jQuery UI lightness Theme you have to include its stylesheet:
```html
<link rel="stylesheet" type="text/css" href="candy/plugins/jquery-ui/ui-lightness/css/ui-lightness.css" />
```
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
html, body {
font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;
}
div#candy {
background-color: #EEE;
}
div#candy div#chat-pane ul#chat-tabs {
background-image: url('../images/ui-bg_gloss-wave_35_f6a828_500x100.png');
background-repeat: repeat-x;
background-color: #F6A828;
}
div#candy div#chat-pane ul#chat-tabs li {
border-right: 1px solid #CCC;
}
div#candy div#chat-pane ul#chat-tabs li a {
color: #1C94C4;
}
div#candy div#chat-pane ul#chat-tabs li.active a {
color: #E78F08;
}
div#candy div#chat-pane ul#chat-tabs li.roomtype-chat small.unread {
background-color: #F6A828;
}
div#candy div#chat-pane ul#chat-tabs li.roomtype-groupchat small.unread {
background-color: #F6A828;
}
div#candy div#chat-pane ul#chat-toolbar {
background-color: #EEE;
border-top: 1px solid #DDD;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane form.message-form input.submit {
padding: 2px 5px 5px 5px;
background-color: #F6F6F6;
border: 1px solid #CCC;
border-radius: 4px;
color: #1C94C4;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane form.message-form input.submit:hover {
background-color: #FDF9E1;
border: 1px solid #FBCB09;
color: #E78F08;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane div.message-form-wrapper {
border-top: 1px solid #DDD;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane div.message-pane-wrapper dl.message-pane dd span.label a.name {
color: #888;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane div.message-pane-wrapper dl.message-pane dd.adminmessage {
color: #000;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane div.message-pane-wrapper dl.message-pane dd.subject {
color: #E78F08;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane div.roster-pane div.user {
background-color: #F8F8F8;
border: 1px solid #CCC;
border-radius: 4px;
color: #1C94C4;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane div.roster-pane div.user:hover {
cursor: pointer;
background-color: #FDF9E1;
border: 1px solid #FBCB09;
color: #E78F08;
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane div.roster-pane ul li.context {
background-image: url('../images/action/menu.png');
}
div#candy div#chat-pane div#chat-rooms.rooms div.room-pane div.roster-pane ul li.context:hover {
background-image: url('../images/action/menu-hover.png');
background-color: #F6A828;
}
#context-menu {
position: absolute;
z-index: 10;
display: none;
padding: 15px 10px;
margin: 8px -28px -8px -12px;
background: url('../images/context-arrows.gif') no-repeat left bottom;
}
#context-menu ul {
background-color: #F8F8F8;
border: 1px solid #CCC;
border-radius: 4px;
color: #1C94C4;
}
#context-menu li:hover {
background-color: #FDF9E1 !important;
color: #E78F08;
}
#chat-modal {
background: url('../images/modal-bg.png');
color: #000;
}
#chat-modal a#admin-message-cancel.close {
color: #000;
}
#chat-modal a#admin-message-cancel.close:hover {
background-color: #F6A828;
color: #FFF;
}
#chat-modal-overlay {
background-image: url('../images/overlay.png');
filter:alpha(opacity=50);
opacity: 0.5;
-moz-opacity:0.5;
}
#tooltip {
background: url('../images/tooltip-arrows.gif') no-repeat left bottom;
}
#tooltip div {
background-color: #F6A828;
color: #FFF;
}
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
# Reply Highlighting
To better support conversations in high-activity rooms, this plugin highlights any message that contains "@yourusername"
## Usage
```HTML
<script type="text/javascript" src="candyshop/replies/candy.js"></script>
<link rel="stylesheet" type="text/css" href="candyshop/replies/candy.css" />
```
```JavaScript
CandyShop.Replies.init();
```
.message-pane dt {
height: 13px;
}
.message-pane dd.mention, .message-pane dt.mention {
background-color: #FFF7DE;
}
\ No newline at end of file
/*
* candy-replies-plugin
* @version 0.1 (2013-2-20)
* @author Drew Harry (drew.harry@gmail.com)
*
* Adds @reply highlighting to chat messages to help with high velocity
* conversations.
*/
var CandyShop = (function(self) { return self; }(CandyShop || {}));
CandyShop.Replies = (function(self, Candy, $) {
self.init = function() {
Candy.View.Event.Message.onShow = handleOnShow;
return self;
};
var handleOnShow = function(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
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:
```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:
```JavaScript
Candy.init('/http-bind/');
// enable RoomPanel plugin
CandyShop.RoomPanel.init({
// domain that hosts the muc rooms, only required if autoDetectRooms is enabled
mucDomain: 'conference.yourdomain.com',
// allow you to force a list of rooms, only required if autoDetectRoom is disabled
roomList: [
{
name: 'my room',
jid: 'my-room@conference.yourdomain.com'
},
{
name: 'other room',
jid: 'other-room@conference.yourdomain.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]
autoDetectRooms: true,
// how long in seconds before refreshing room list, default value is 600. [optional]
roomCacheTime: 600
});
Candy.Core.connect();
.roomList a {
color: #AAA;
}
#roomPanel-control {
width: 16px;
background: url(images/room.png);
}
This diff was suppressed by a .gitattributes entry.
var CandyShop = (function(self) {return self;}(CandyShop || {}));
/**
* Class: Shows a list of rooms upon connection and adds a little icon to bring list of rooms
*/
CandyShop.RoomPanel = (function(self, Candy, Strophe, $) {
var _options = {
// domain that hosts the muc rooms, only required if autoDetectRooms is enabled
mucDomain: '',
// allow you to force a list of rooms, only required if autoDetectRoom is disabled
roomList: [],
// 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]
autoDetectRooms: true,
// how long in seconds before refreshing room list, default value is 600. [optional]
roomCacheTime: 600
};
var _lastRoomUpdate = 0;
self.init = function(options) {
$.extend(_options, options);
self.applyTranslations();
/* Overwrite candy allTabsClosed function not
* to disconnect when all tags are closed */
if (_options.showIfAllTabClosed) {
Candy.View.Pane.Chat.allTabsClosed = function () {
CandyShop.RoomPanel.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();
});
$(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
} //if
return true;
}});
};
self.showRoomPanelIfAllClosed = function() {
var roomCount = 0;
var rooms = Candy.Core.getRooms();
for (k in rooms) {
if (rooms.hasOwnProperty(k)) {
roomCount++;
} //if
} //for
if (roomCount == 0) {
CandyShop.RoomPanel.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
});
});
_options.roomList = newRoomList;
_lastRoomUpdate = Math.round(new Date().getTime() / 1000);
self.showRoomPanel();
};
self.showRoomPanel = function() {
/* call until connecting modal is gone */
if ($('#chat-modal').is(':visible')) {
setTimeout(CandyShop.RoomPanel.showRoomPanel, 100);
} else {
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'})
.c('query', {xmlns: Strophe.NS.DISCO_ITEMS});
Candy.Core.getConnection().sendIQ(iq, self.updateRoomList);
} else {
var html = Mustache.to_html(CandyShop.RoomPanel.Template.rooms, {
title: $.i18n._('candyshopRoomPanelChooseRoom'),
roomList: _options.roomList
});
Candy.View.Pane.Chat.Modal.show(html,true);
$('.roomList a').bind('click', function(e) {
var roomJid = this.href.split('#')[1];
Candy.Core.Action.Jabber.Room.Join(roomJid);
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';
};
return self;
}(CandyShop.RoomPanel || {}, Candy, Strophe, jQuery));
CandyShop.RoomPanel.Template = (function (self) {
var roomParts = [
'<div class="roomList">',
'<h2>{{title}}</h2>',
'<ul>',
'{{#roomList}}',
'<li><a href="#{{jid}}">{{name}}</a></li>',
'{{/roomList}}',
'</ul>',
'</div>'
];
self.rooms = roomParts.join('');
return self;
})(CandyShop.RoomPanel.Template || {});
This diff was suppressed by a .gitattributes entry.
#Candy Timeago plugin
This plugin replaces the exact time/date with 'fuzzy timestamps' (e.g. 'less than a minute ago', '2 minutes ago', 'about an hour ago'). The timestamps update dynamically. All the heavy lifting is done by Ryan McGeary's excellent jQuery Timeago plugin (http://timeago.yarp.com/).
##Usage
To enable Timeago include it's JavaScript code and CSS file (after the main Candy script and CSS):
```html
<script type="text/javascript" src="candyshop/timeago/candy.js"></script>
<link rel="stylesheet" type="text/css" href="candyshop/timeago/candy.css" />
```
Then call its init() method after Candy has been initialized:
```html
Candy.init('/http-bind/');
CandyShop.Timeago.init();
Candy.Core.connect();
```
\ No newline at end of file
.message-pane dt {
width: 120px;
padding-right: 10px;
}
.message-pane dt abbr {
border-bottom: none;
}
\ No newline at end of file
/*
* candy-timeago-plugin
* @version 0.1 (2011-07-15)
* @author David Devlin (dave.devlin@gmail.com)
*
* Integrates the jQuery Timeago plugin (http://timeago.yarp.com/) with Candy.
*/
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.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();
};
};
return self;
}(CandyShop.Timeago || {}, Candy, jQuery));
/*
* timeago: a jQuery plugin, version: 0.9.3 (2011-01-21)
* @requires jQuery v1.2.3 or later
*
* Timeago is a jQuery plugin that makes it easy to support automatically
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
*
* For usage and examples, visit:
* http://timeago.yarp.com/
*
* Licensed under the MIT:
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright (c) 2008-2011, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
*/
(function($) {
$.timeago = function(timestamp) {
if (timestamp instanceof Date) {
return inWords(timestamp);
} else if (typeof timestamp === "string") {
return inWords($.timeago.parse(timestamp));
} else {
return inWords($.timeago.datetime(timestamp));
}
};
var $t = $.timeago;
$.extend($.timeago, {
settings: {
refreshMillis: 60000,
allowFuture: false,
strings: {
prefixAgo: null,
prefixFromNow: null,
suffixAgo: "ago",
suffixFromNow: "from now",
seconds: "less than a minute",
minute: "about a minute",
minutes: "%d minutes",
hour: "about an hour",
hours: "about %d hours",
day: "a day",
days: "%d days",
month: "about a month",
months: "%d months",
year: "about a year",
years: "%d years",
numbers: []
}
},
inWords: function(distanceMillis) {
var $l = this.settings.strings;
var prefix = $l.prefixAgo;
var suffix = $l.suffixAgo;
if (this.settings.allowFuture) {
if (distanceMillis < 0) {
prefix = $l.prefixFromNow;
suffix = $l.suffixFromNow;
}
distanceMillis = Math.abs(distanceMillis);
}
var seconds = distanceMillis / 1000;
var minutes = seconds / 60;
var hours = minutes / 60;
var days = hours / 24;
var years = days / 365;
function substitute(stringOrFunction, number) {
var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
var value = ($l.numbers && $l.numbers[number]) || number;
return string.replace(/%d/i, value);
}
var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
seconds < 90 && substitute($l.minute, 1) ||
minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
minutes < 90 && substitute($l.hour, 1) ||
hours < 24 && substitute($l.hours, Math.round(hours)) ||
hours < 48 && substitute($l.day, 1) ||
days < 30 && substitute($l.days, Math.floor(days)) ||
days < 60 && substitute($l.month, 1) ||
days < 365 && substitute($l.months, Math.floor(days / 30)) ||
years < 2 && substitute($l.year, 1) ||
substitute($l.years, Math.floor(years));
return $.trim([prefix, words, suffix].join(" "));
},
parse: function(iso8601) {
var s = $.trim(iso8601);
s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
s = s.replace(/-/,"/").replace(/-/,"/");
s = s.replace(/T/," ").replace(/Z/," UTC");
s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
return new Date(s);
},
datetime: function(elem) {
// jQuery's `is()` doesn't play well with HTML5 in IE
var isTime = $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
var iso8601 = isTime ? $(elem).attr("datetime") : $(elem).attr("title");
return $t.parse(iso8601);
}
});
$.fn.timeago = function() {
var self = this;
self.each(refresh);
var $s = $t.settings;
if ($s.refreshMillis > 0) {
setInterval(function() { self.each(refresh); }, $s.refreshMillis);
}
return self;
};
function refresh() {
var data = prepareData(this);
if (!isNaN(data.datetime)) {
$(this).text(inWords(data.datetime));
}
return this;
}
function prepareData(element) {
element = $(element);
if (!element.data("timeago")) {
element.data("timeago", { datetime: $t.datetime(element) });
var text = $.trim(element.text());
if (text.length > 0) {
element.attr("title", text);
}
}
return element.data("timeago");
}
function inWords(date) {
return $t.inWords(distance(date));
}
function distance(date) {
return (new Date().getTime() - date.getTime());
}
// fix for IE6 suckage
document.createElement("abbr");
document.createElement("time");
}(jQuery));
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Candy - Chats are not dead yet</title>
<link rel="shortcut icon" href="res/img/favicon.png" type="image/gif"/>
<link rel="stylesheet" type="text/css" href="res/default.css"/>
<script type="text/javascript" src="jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="purl.js"></script>
<script type="text/javascript" src="libs.min.js"></script>
<script type="text/javascript" src="candy.min.js"></script>
<script type="text/javascript" src="candy.fix.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" />
<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" />
<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" />
<script type="text/javascript">
$(document).ready(function () {
var url = $.url();
Candy.Util.setCookie('candy-nostatusmessages', '1', 365);
Candy.init(url.param('bosh'), {
core: { debug: false, autojoin: ['shinkirou@conference.my-card.in'], disableWindowUnload: true },
view: { resources: 'res/', language: 'cn' }
});
CandyShop.InlineImages.init();
CandyShop.NotifyMe.init();
CandyShop.NameComplete.init();
Candy.Core.connect(url.param('jid'), url.param('password'));
$(Candy).on('candy:core.chat.connection', function(event, args){
if(args.status == Strophe.Status.CONNECTED){
Candy.Core.Action.Jabber.Roster()
//Candy.Core.getConnection().send($iq({type: 'get'}).c('vCard', {xmlns: 'vcard-temp'}).tree());
}
});
Candy.Core.addHandler(function (msg) {
parent.postMessage({type: 'roster', stanza: $('<div />').append(msg).html()}, '*')
return true
}, 'jabber:iq:roster', 'iq', 'result', null, null)
Candy.Core.addHandler(function (msg) {
Candy.Core.getConnection().send($iq({ type: 'result', id: msg.getAttribute('id') }));
parent.postMessage({type: 'roster_set', stanza: $('<div />').append(msg).html()}, '*')
return true
}, 'jabber:iq:roster', 'iq', 'set', null, null)
Candy.Core.addHandler(function (msg) {
parent.postMessage({type: 'vcard', stanza: $('<div />').append(msg).html()}, '*')
return true
}, 'vcard-temp', 'iq', 'result', null, null)
$(Candy).on('candy:core.presence', function(event, args){
parent.postMessage({type: 'presence', from: args.from, stanza: $('<div />').append(args.stanza).html()}, '*')
});
});
</script>
</head>
<body>
<div id="candy"></div>
<script type="text/javascript">
window.addEventListener('message', function (event) {
var msg = event.data
switch (msg.type) {
case 'chat':
Candy.View.Pane.PrivateRoom.open(msg.jid, msg.jid.split('@')[0], true, true);
break;
case 'subscribe':
Candy.Core.getConnection().send($iq({type: 'set', to: msg.jid}).c('query', {xmlns: 'jabber:iq:roster'}).c('item', {jid: msg.jid}).tree());
Candy.Core.getConnection().send($pres({ to: msg.jid, type: "subscribe" }));
break;
case 'subscribed':
Candy.Core.getConnection().send($pres({ to: msg.jid, type: "subscribed" }));
break;
case 'unsubscribe':
Candy.Core.getConnection().send($pres({ to: msg.jid, type: "unsubscribed" }));
break;
case 'vcard':
Candy.Core.getConnection().send($iq({type: 'get', to: msg.jid}).c('vCard', {xmlns: 'vcard-temp'}).tree());
break;
}
}, false);
</script>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* Purl (A JavaScript URL parser) v2.3.1
* Developed and maintanined by Mark Perkins, mark@allmarkedup.com
* Source repository: https://github.com/allmarkedup/jQuery-URL-Parser
* Licensed under an MIT-style license. See https://github.com/allmarkedup/jQuery-URL-Parser/blob/master/LICENSE for details.
*/
;(function(factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else {
window.purl = factory();
}
})(function() {
var tag2attr = {
a : 'href',
img : 'src',
form : 'action',
base : 'href',
script : 'src',
iframe : 'src',
link : 'href',
embed : 'src',
object : 'data'
},
key = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment'], // keys available to query
aliases = { 'anchor' : 'fragment' }, // aliases for backwards compatability
parser = {
strict : /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, //less intuitive, more accurate to the specs
loose : /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
},
isint = /^[0-9]+$/;
function parseUri( url, strictMode ) {
var str = decodeURI( url ),
res = parser[ strictMode || false ? 'strict' : 'loose' ].exec( str ),
uri = { attr : {}, param : {}, seg : {} },
i = 14;
while ( i-- ) {
uri.attr[ key[i] ] = res[i] || '';
}
// build query and fragment parameters
uri.param['query'] = parseString(uri.attr['query']);
uri.param['fragment'] = parseString(uri.attr['fragment']);
// split path and fragement into segments
uri.seg['path'] = uri.attr.path.replace(/^\/+|\/+$/g,'').split('/');
uri.seg['fragment'] = uri.attr.fragment.replace(/^\/+|\/+$/g,'').split('/');
// compile a 'base' domain attribute
uri.attr['base'] = uri.attr.host ? (uri.attr.protocol ? uri.attr.protocol+'://'+uri.attr.host : uri.attr.host) + (uri.attr.port ? ':'+uri.attr.port : '') : '';
return uri;
}
function getAttrName( elm ) {
var tn = elm.tagName;
if ( typeof tn !== 'undefined' ) return tag2attr[tn.toLowerCase()];
return tn;
}
function promote(parent, key) {
if (parent[key].length === 0) return parent[key] = {};
var t = {};
for (var i in parent[key]) t[i] = parent[key][i];
parent[key] = t;
return t;
}
function parse(parts, parent, key, val) {
var part = parts.shift();
if (!part) {
if (isArray(parent[key])) {
parent[key].push(val);
} else if ('object' == typeof parent[key]) {
parent[key] = val;
} else if ('undefined' == typeof parent[key]) {
parent[key] = val;
} else {
parent[key] = [parent[key], val];
}
} else {
var obj = parent[key] = parent[key] || [];
if (']' == part) {
if (isArray(obj)) {
if ('' !== val) obj.push(val);
} else if ('object' == typeof obj) {
obj[keys(obj).length] = val;
} else {
obj = parent[key] = [parent[key], val];
}
} else if (~part.indexOf(']')) {
part = part.substr(0, part.length - 1);
if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
parse(parts, obj, part, val);
// key
} else {
if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
parse(parts, obj, part, val);
}
}
}
function merge(parent, key, val) {
if (~key.indexOf(']')) {
var parts = key.split('[');
parse(parts, parent, 'base', val);
} else {
if (!isint.test(key) && isArray(parent.base)) {
var t = {};
for (var k in parent.base) t[k] = parent.base[k];
parent.base = t;
}
if (key !== '') {
set(parent.base, key, val);
}
}
return parent;
}
function parseString(str) {
return reduce(String(str).split(/&|;/), function(ret, pair) {
try {
pair = decodeURIComponent(pair.replace(/\+/g, ' '));
} catch(e) {
// ignore
}
var eql = pair.indexOf('='),
brace = lastBraceInKey(pair),
key = pair.substr(0, brace || eql),
val = pair.substr(brace || eql, pair.length);
val = val.substr(val.indexOf('=') + 1, val.length);
if (key === '') {
key = pair;
val = '';
}
return merge(ret, key, val);
}, { base: {} }).base;
}
function set(obj, key, val) {
var v = obj[key];
if (typeof v === 'undefined') {
obj[key] = val;
} else if (isArray(v)) {
v.push(val);
} else {
obj[key] = [v, val];
}
}
function lastBraceInKey(str) {
var len = str.length,
brace,
c;
for (var i = 0; i < len; ++i) {
c = str[i];
if (']' == c) brace = false;
if ('[' == c) brace = true;
if ('=' == c && !brace) return i;
}
}
function reduce(obj, accumulator){
var i = 0,
l = obj.length >> 0,
curr = arguments[2];
while (i < l) {
if (i in obj) curr = accumulator.call(undefined, curr, obj[i], i, obj);
++i;
}
return curr;
}
function isArray(vArg) {
return Object.prototype.toString.call(vArg) === "[object Array]";
}
function keys(obj) {
var key_array = [];
for ( var prop in obj ) {
if ( obj.hasOwnProperty(prop) ) key_array.push(prop);
}
return key_array;
}
function purl( url, strictMode ) {
if ( arguments.length === 1 && url === true ) {
strictMode = true;
url = undefined;
}
strictMode = strictMode || false;
url = url || window.location.toString();
return {
data : parseUri(url, strictMode),
// get various attributes from the URI
attr : function( attr ) {
attr = aliases[attr] || attr;
return typeof attr !== 'undefined' ? this.data.attr[attr] : this.data.attr;
},
// return query string parameters
param : function( param ) {
return typeof param !== 'undefined' ? this.data.param.query[param] : this.data.param.query;
},
// return fragment parameters
fparam : function( param ) {
return typeof param !== 'undefined' ? this.data.param.fragment[param] : this.data.param.fragment;
},
// return path segments
segment : function( seg ) {
if ( typeof seg === 'undefined' ) {
return this.data.seg.path;
} else {
seg = seg < 0 ? this.data.seg.path.length + seg : seg - 1; // negative segments count from the end
return this.data.seg.path[seg];
}
},
// return fragment segments
fsegment : function( seg ) {
if ( typeof seg === 'undefined' ) {
return this.data.seg.fragment;
} else {
seg = seg < 0 ? this.data.seg.fragment.length + seg : seg - 1; // negative segments count from the end
return this.data.seg.fragment[seg];
}
}
};
}
purl.jQuery = function($){
if ($ != null) {
$.fn.url = function( strictMode ) {
var url = '';
if ( this.length ) {
url = $(this).attr( getAttrName(this[0]) ) || '';
}
return purl( url, strictMode );
};
$.url = purl;
}
};
purl.jQuery(window.jQuery);
return purl;
});
This diff is collapsed.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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