Comparar commits
48 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 7fe6758c6d | |||
| d0508b4b7f | |||
| 3f75e46a67 | |||
| 5f158cc907 | |||
| 5a58becdea | |||
| 1be565833d | |||
| bd0f2562df | |||
| 5f67201e39 | |||
| 053795c875 | |||
| 4cd2d4f406 | |||
| e43ecf2c82 | |||
| dc8a4ab000 | |||
| 00de205459 | |||
| 263b00b5cd | |||
| 409199ea97 | |||
| 3d667cf94b | |||
| df28002475 | |||
| 1793897395 | |||
| e90343e072 | |||
| 3a2b35f2db | |||
| e070dd3b76 | |||
| 20a765be7f | |||
| 46fdfb74c3 | |||
| a16b250bd1 | |||
| 115480d535 | |||
| bf45db98a5 | |||
| 1d66d44a5d | |||
| 8338810e21 | |||
| 5f8c4d02c6 | |||
| 1806a93762 | |||
| 728711939f | |||
| 78ac74f202 | |||
| b7b5a874c4 | |||
| 27255ac23d | |||
| 67927622fb | |||
| a73d883a76 | |||
| 0b921b5afa | |||
| 065d5f84a7 | |||
| 833883928a | |||
| 7fd52d5454 | |||
| 1308dca732 | |||
| 3da4903542 | |||
| 2fe92d49be | |||
| 1d34639a7c | |||
| 7b44b2aede | |||
| 7a33c1a039 | |||
| 02d51c63ae | |||
| 18b44b6312 |
@@ -0,0 +1,7 @@
|
||||
local/*
|
||||
*.local.*
|
||||
*.local/
|
||||
server/libs/daemon/daemon.node
|
||||
server/libs/daemon/.lock-wscript
|
||||
client/js/im.compiled.js
|
||||
client/js/im.min.js
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2005 - 2010 Joshua Gross, http://ajaxim.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,44 @@
|
||||
SRC_DIR = client/js
|
||||
BUILD_DIR = build
|
||||
|
||||
PREFIX = .
|
||||
DIST_DIR = client/js
|
||||
|
||||
BASE_FILES = ${SRC_DIR}/md5.js\
|
||||
${SRC_DIR}/store.js\
|
||||
${SRC_DIR}/cookies.js\
|
||||
${SRC_DIR}/dateformat.js\
|
||||
${SRC_DIR}/json.js\
|
||||
${SRC_DIR}/im.js
|
||||
|
||||
MODULES = ${SRC_DIR}/intro.js\
|
||||
${BASE_FILES}\
|
||||
${SRC_DIR}/outro.js
|
||||
|
||||
IMJS = ${DIST_DIR}/im.compiled.js
|
||||
IMJS_MIN = ${DIST_DIR}/im.min.js
|
||||
|
||||
COMPILER = java -jar ${BUILD_DIR}/google-compiler-20100616.jar
|
||||
|
||||
all: imjs min
|
||||
@@echo "Ajax IM build complete."
|
||||
|
||||
imjs: ${IMJS}
|
||||
|
||||
${IMJS}: ${MODULES}
|
||||
@@echo "Building" ${IMJS}
|
||||
@@cat ${MODULES} | sed 's/debug = true/debug = false/' > ${IMJS}
|
||||
|
||||
min: ${IMJS_MIN}
|
||||
|
||||
${IMJS_MIN}: ${IMJS}
|
||||
@@echo "Building" ${IMJS_MIN}
|
||||
|
||||
@@head -21 ${IMJS} > ${IMJS_MIN}
|
||||
@@${COMPILER} --js ${IMJS} --warning_level QUIET >> ${IMJS_MIN}
|
||||
@@rm -f ${IMJS}
|
||||
|
||||
clean:
|
||||
@@echo "Removing built files"
|
||||
@@rm -f ${IMJS}
|
||||
@@rm -f ${IMJS_MIN}
|
||||
@@ -1,2 +1,205 @@
|
||||
# What is Ajax IM?
|
||||
**Ajax IM ("Ajax Instant Messenger") is a browser-centric instant messaging framework.** It uses AJAX to create a real-time (or near real-time) IM environment that can be used in conjunction with existing community and commercial software, or simply as a stand-alone product.
|
||||
# Ajax IM
|
||||
|
||||
## What is Ajax IM?
|
||||
|
||||
Ajax IM ("Ajax Instant Messenger") is an instant messenger for the web. It is
|
||||
intended to be integrated with existing websites to provide a more interactive
|
||||
community experience.
|
||||
|
||||
## Features
|
||||
|
||||
* Instant communication between connected users
|
||||
* Robust backend server built on [Node.js](http://nodejs.org)
|
||||
* Can be integrated with virtually any existing website
|
||||
* Seamless authentication
|
||||
* Works across an entire website, unobtrusively
|
||||
* Automatic state management between page reloads
|
||||
* User state and statuses
|
||||
* and _much_ more!
|
||||
|
||||
## Installation
|
||||
|
||||
Install `Node.js`:
|
||||
|
||||
wget http://nodejs.org/dist/node-latest.tar.gz
|
||||
tar xzf node-latest.tar.gz
|
||||
cd node-latest
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
Install Node Package Manager (`npm`): see instructions on the [npm](http://github.com/isaacs/npm) page.
|
||||
|
||||
Install `Socket.io`:
|
||||
|
||||
npm install socket-io
|
||||
|
||||
That's it!
|
||||
|
||||
## More Information
|
||||
|
||||
* Follow [endtwist](http://twitter.com/endtwist) on twitter for updates
|
||||
* [Google Group](http://groups.google.com/group/ajaxim) for discussion
|
||||
* [GitHub Wiki](https://github.com/endtwist/AjaxIM/wiki) for guidance
|
||||
|
||||
## Node Compatibility
|
||||
|
||||
The `socket-io` branch of Ajax IM is compatible with node --version:
|
||||
|
||||
v0.4.6
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull requests are being accepted! If you would like to contribute, simply fork
|
||||
the project and make your changes.
|
||||
|
||||
### Style Guide
|
||||
|
||||
If you intend on contributing, please follow this style guide when submitting
|
||||
patches or commits. Submissions that do not follow these guidelines will not
|
||||
be accepted.
|
||||
|
||||
* Use 4 space indents (not tabs!)
|
||||
* No trailing whitespace
|
||||
* Blank line at the end of files
|
||||
* Semi-colons at the ends of lines, where appropriate
|
||||
* Keep lines to 80 characters or less
|
||||
* Never bump the version
|
||||
|
||||
No whitespace between keys and values:
|
||||
{foo: 'bar'}
|
||||
// good
|
||||
|
||||
{foo : 'bar'}
|
||||
// bad
|
||||
|
||||
Hash formatting:
|
||||
{foo: 'bar', baz: 'taz'}
|
||||
// good
|
||||
|
||||
{
|
||||
foo: 'bar',
|
||||
baz: 'taz',
|
||||
moo: 'cow'
|
||||
}
|
||||
// good
|
||||
|
||||
{ foo: 'bar', baz: 'taz' }
|
||||
// bad
|
||||
|
||||
{foo: 'bar',
|
||||
baz: 'taz',
|
||||
moo: 'cow'}
|
||||
// bad
|
||||
|
||||
Chained methods:
|
||||
str
|
||||
.strip
|
||||
.replace(...)
|
||||
.replace(...)
|
||||
// good
|
||||
|
||||
str
|
||||
.strip
|
||||
.replace(...)
|
||||
.replace(...)
|
||||
// bad
|
||||
|
||||
str.
|
||||
strip.
|
||||
replace(...).
|
||||
replace(...)
|
||||
// bad
|
||||
|
||||
Single quotes over double quotes, unless double quotes make sense:
|
||||
'hello'
|
||||
// good
|
||||
|
||||
"what's up?"
|
||||
// good
|
||||
|
||||
"hello"
|
||||
// bad
|
||||
|
||||
Ternary expressions are fine, but cannot be nested and must be formatted as:
|
||||
foo = (a ? b : c);
|
||||
// good
|
||||
|
||||
foo = (something.hasAPropertyLikeThis == 'some other value'
|
||||
? 'result one'
|
||||
: 'result two'
|
||||
);
|
||||
// good
|
||||
|
||||
foo = something.hasAPropertyLikeThis === 'some other value' ?
|
||||
'result one' :
|
||||
'result two'
|
||||
// bad
|
||||
|
||||
Use braces for conditionals, unless conditionals are single statements:
|
||||
if(foo) {
|
||||
bar();
|
||||
baz();
|
||||
}
|
||||
// good
|
||||
|
||||
if(foo) bar();
|
||||
else baz();
|
||||
// good
|
||||
|
||||
if(foo)
|
||||
bar(),
|
||||
baz()
|
||||
// bad
|
||||
|
||||
Closures:
|
||||
function() {
|
||||
}
|
||||
// good
|
||||
|
||||
function () {
|
||||
}
|
||||
// bad
|
||||
|
||||
function()
|
||||
{
|
||||
}
|
||||
// bad
|
||||
|
||||
Methods:
|
||||
foo.bar = function() {
|
||||
}
|
||||
// good
|
||||
|
||||
foo.bar = function (){
|
||||
}
|
||||
// bad
|
||||
|
||||
foo.bar = function()
|
||||
{
|
||||
}
|
||||
// bad
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2010 [Joshua Gross](http://www.unwieldy.net)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
@@ -1,81 +0,0 @@
|
||||
<?php
|
||||
// = ajaxim.php =
|
||||
//
|
||||
// **Copyright © 2005 – 2010 Joshua Gross**\\
|
||||
// //MIT Licensed//
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
require_once('config.php');
|
||||
require_once('libraries/db/base.php');
|
||||
require_once('libraries/server/base.php');
|
||||
|
||||
# import the database library
|
||||
require_once('libraries/db/' . DB_ENGINE . '.php');
|
||||
$db_class = DB_ENGINE . '_Database';
|
||||
|
||||
if (!function_exists('class_alias')) {
|
||||
// Allows us to alias Database library classes to standardized names
|
||||
function class_alias($original, $alias) {
|
||||
eval('class ' . $alias . ' extends ' . $original . ' {}');
|
||||
}
|
||||
}
|
||||
|
||||
class_alias(DB_ENGINE . '_Database', 'Database');
|
||||
class_alias(DB_ENGINE . '_User', 'User');
|
||||
class_alias(DB_ENGINE . '_Status', 'Status');
|
||||
class_alias(DB_ENGINE . '_Message', 'Message');
|
||||
class_alias(DB_ENGINE . '_Friend', 'Friend');
|
||||
|
||||
// Fix problems with magic_quotes_gpc/sybase
|
||||
if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {
|
||||
$syb = strtolower(ini_get('magic_quotes_sybase'));
|
||||
if(empty($syb) || $syb == 'off') {
|
||||
foreach($_POST as $key => $val) $_POST[$key] = stripslashes($val);
|
||||
foreach($_GET as $key => $val) $_GET[$key] = stripslashes($val);
|
||||
foreach($_COOKIE as $key => $val) $_COOKIE[$key] = stripslashes($val);
|
||||
} else {
|
||||
foreach($_POST as $key => $val) $_POST[$key] = str_replace("''", "'", $val);
|
||||
foreach($_GET as $key => $val) $_GET[$key] = str_replace("''", "'", $val);
|
||||
foreach($_COOKIE as $key => $val) $_COOKIE[$key] = str_replace("''", "'", $val);
|
||||
}
|
||||
}
|
||||
|
||||
// import the server library
|
||||
require_once('libraries/server/' . IM_LIBRARY . '.php');
|
||||
$im_class = IM_LIBRARY . '_IM';
|
||||
$im = new $im_class();
|
||||
$action = preg_replace('/^' . preg_quote($_SERVER['SCRIPT_NAME'], '/') . '\/(.+?)(\?.+)?$/', '\1', $_SERVER['REQUEST_URI']);
|
||||
|
||||
if(substr($action, 0, 1) != '_' && method_exists($im, $action))
|
||||
if($action == 'poll') {
|
||||
if($_GET['method'] == 'comet') {
|
||||
$im->poll('comet');
|
||||
} else {
|
||||
print $_GET['callback'] . '(' . json_encode($im->poll($_GET['method'])) . ')';
|
||||
}
|
||||
} else {
|
||||
$execute = call_user_func_array(array($im, $action), $_POST);
|
||||
if($execute)
|
||||
print json_encode($execute !== false ? $execute : array('e'=>'wrong args'));
|
||||
}
|
||||
else
|
||||
print json_encode(array('e'=>'no method'));
|
||||
|
||||
/* End of ajaxim.php */
|
||||
@@ -1,93 +0,0 @@
|
||||
Node Server Memcached Protocol
|
||||
for logging users in and out of the server.
|
||||
-------------------------------------------
|
||||
|
||||
add <username> <!ignore flags> <!ignore exptime> <bytes> [!ignore noreply]\r\n
|
||||
|
||||
- <username> is the username of the new user
|
||||
|
||||
- <flags> and <exptime> are ignored.
|
||||
|
||||
- <bytes> is the number of bytes in the data block to follow, *not*
|
||||
including the delimiting \r\n. <bytes> may *not* be zero.
|
||||
|
||||
After this line, the client sends the JSON-encoded data block:
|
||||
|
||||
<data block>\r\n
|
||||
|
||||
- <data block> is JSON-encoded information about the user, having a length
|
||||
of <bytes> bytes.
|
||||
|
||||
After sending the command line and the data block, the client awaits
|
||||
the reply, which may be:
|
||||
|
||||
- "STORED\r\n", to indicate that the user is or has been logged in.
|
||||
|
||||
- "NOT_STORED\r\n", to indicate that there was a problem storing the
|
||||
new user data.
|
||||
|
||||
Retrieval command:
|
||||
------------------
|
||||
|
||||
The retrieval commands "get" and "gets" operates like this:
|
||||
|
||||
get <type/identifier>\r\n
|
||||
gets <type/identifier> <type/identifier> <...>\r\n
|
||||
|
||||
- <type/identifier> is one of:
|
||||
- session/<session_id> where <session_id> is the session of an
|
||||
existing user.
|
||||
> returns information about the user
|
||||
- username/<username> where <username> is the (properly formatted)
|
||||
username of a logged in user.
|
||||
> returns information about the user
|
||||
- list/
|
||||
> returns a list of all online users
|
||||
- online/
|
||||
> returns an integer value of the online user count
|
||||
|
||||
After this command, the client expects zero or more items, each of
|
||||
which is received as a text line followed by a data block. After all
|
||||
the items have been transmitted, the server sends the string
|
||||
|
||||
"END\r\n"
|
||||
|
||||
to indicate the end of response.
|
||||
|
||||
Each item sent by the server looks like this:
|
||||
|
||||
VALUE <key> <flags> <bytes>\r\n
|
||||
<data block>\r\n
|
||||
|
||||
- <key> is the key for the item being sent
|
||||
|
||||
- <flags> is set to 0
|
||||
|
||||
- <bytes> is the length of the data block to follow, *not* including
|
||||
its delimiting \r\n
|
||||
|
||||
- <data block> is the data for this response, usually in JSON format (exception: online/).
|
||||
|
||||
If some of the keys appearing in a retrieval request are not sent back
|
||||
by the server in the item list this means that the server does not
|
||||
hold items with such keys (because they were never stored, or stored
|
||||
but deleted to make space for more items, or expired, or explicitly
|
||||
deleted by a client).
|
||||
|
||||
Logging out
|
||||
--------
|
||||
|
||||
The command "delete" allows for explicit logging out of users:
|
||||
|
||||
delete <username>\r\n
|
||||
|
||||
- <username> is the username of the user the client wishes the server
|
||||
to log out.
|
||||
|
||||
The response line to this command can be one of:
|
||||
|
||||
- "DELETED\r\n" to indicate success
|
||||
|
||||
- "NOT_FOUND\r\n" to indicate that the user with this username was not
|
||||
found.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Adapted from
|
||||
* https://github.com/jaz303/jquery-grab-bag/blob/master/javascripts/jquery.autogrow-textarea.js
|
||||
*
|
||||
* Auto-growing textareas; technique ripped from Facebook
|
||||
*/
|
||||
$.fn.autogrow = function(options) {
|
||||
|
||||
this.filter('textarea').each(function() {
|
||||
|
||||
var $this = $(this),
|
||||
minHeight = $this.height(),
|
||||
lineHeight = $this.css('lineHeight');
|
||||
|
||||
var shadow = $('<div></div>').css({
|
||||
position: 'absolute',
|
||||
top: -10000,
|
||||
left: -10000,
|
||||
width: $(this).width() - parseInt($this.css('paddingLeft')) - parseInt($this.css('paddingRight')),
|
||||
fontSize: $this.css('fontSize'),
|
||||
fontFamily: $this.css('fontFamily'),
|
||||
lineHeight: $this.css('lineHeight'),
|
||||
resize: 'none',
|
||||
'word-wrap':'break-word'
|
||||
}).appendTo(document.body);
|
||||
|
||||
var update = function() {
|
||||
var times = function(string, number) {
|
||||
for (var i = 0, r = ''; i < number; i ++) r += string;
|
||||
return r;
|
||||
};
|
||||
|
||||
var val = this.value.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/\n$/, '<br/> ')
|
||||
.replace(/\n/g, '<br/>')
|
||||
.replace(/ {2,}/g, function(space) { return times(' ', space.length -1) + ' ' });
|
||||
|
||||
shadow.html(val);
|
||||
$(this).css('height', Math.max(shadow.height(), minHeight));
|
||||
}
|
||||
|
||||
$(this).change(update).keyup(update).keydown(update);
|
||||
|
||||
update.apply(this);
|
||||
|
||||
});
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Cookies API
|
||||
var cookies = {
|
||||
// === {{{AjaxIM.}}}**{{{cookies.set(name, value, days)}}}** ===
|
||||
//
|
||||
// Sets a cookie, stringifying the JSON value upon storing it.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{name}}} is the cookie name.\\
|
||||
// * {{{value}}} is the cookie data that you would like to store.\\
|
||||
// * {{{days}}} is the number of days that the cookie will be stored for.
|
||||
set: function(name, value, days) {
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
var expires = "; expires=" + date.toGMTString();
|
||||
} else var expires = "";
|
||||
document.cookie = name + "=" + JSON.stringify(value) + expires + "; path=/";
|
||||
},
|
||||
|
||||
// === {{{AjaxIM.}}}**{{{cookies.get(name)}}}** ===
|
||||
//
|
||||
// Gets a cookie, decoding the JSON value before returning the data.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{name}}} is the cookie name that you would like to retrieve.
|
||||
get: function(name) {
|
||||
var nameEQ = name + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for(var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) == 0) {
|
||||
var cval = decodeURIComponent(c.substring(nameEQ.length, c.length));
|
||||
return JSON.parse(cval);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// === {{{AjaxIM.}}}**{{{cookies.erase(name)}}}** ===
|
||||
//
|
||||
// Deletes a cookie.
|
||||
//
|
||||
// {{{name}}} is the existing cookie that you would like to delete.
|
||||
erase: function(name) {
|
||||
this.set(name, '', -1);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
// Date Format 1.2.3\\
|
||||
// © 2007-2009 Steven Levithan ([[http://blog.stevenlevithan.com/archives/date-time-format|stevenlevithan.com]])\\
|
||||
// MIT license
|
||||
//
|
||||
// Includes enhancements by Scott Trenda
|
||||
// and Kris Kowal ([[http://cixar.com/~kris.kowal/|cixar.com/~kris.kowal/]])
|
||||
//
|
||||
// Accepts a date, a mask, or a date and a mask and returns a formatted version
|
||||
// of the given date.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{date}}} is a {{{Date()}}} object. If not specified, the date defaults to the
|
||||
// current date/time.
|
||||
// * {{{mask}}} is a string that defines the formatting of the date. Formatting
|
||||
// options can be found in the
|
||||
// [[http://blog.stevenlevithan.com/archives/date-time-format|Date Format]]
|
||||
// documentation. If not specified, the mask defaults to {{{dateFormat.masks.default}}}.
|
||||
|
||||
var dateFormat = function() {
|
||||
var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
|
||||
timezone = new RegExp('\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) ' +
|
||||
'(?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b',
|
||||
'g'),
|
||||
timezoneClip = /[^-+\dA-Z]/g,
|
||||
pad = function (val, len) {
|
||||
val = String(val);
|
||||
len = len || 2;
|
||||
while (val.length < len) val = "0" + val;
|
||||
return val;
|
||||
};
|
||||
|
||||
// Regexes and supporting functions are cached through closure
|
||||
return function (date, mask, utc) {
|
||||
// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
|
||||
if (arguments.length == 1 && Object.prototype.toString.call(date) ==
|
||||
"[object String]" && !/\d/.test(date)) {
|
||||
mask = date;
|
||||
date = undefined;
|
||||
}
|
||||
|
||||
// Passing date through Date applies Date.parse, if necessary
|
||||
date = date ? new Date(date) : new Date;
|
||||
if (isNaN(date)) throw SyntaxError("invalid date");
|
||||
|
||||
mask = String(dateFormat.masks[mask] ||
|
||||
mask ||
|
||||
dateFormat.masks["default"]);
|
||||
|
||||
// Allow setting the utc argument via the mask
|
||||
if (mask.slice(0, 4) == "UTC:") {
|
||||
mask = mask.slice(4);
|
||||
utc = true;
|
||||
}
|
||||
|
||||
var _ = utc ? "getUTC" : "get",
|
||||
d = date[_ + "Date"](),
|
||||
D = date[_ + "Day"](),
|
||||
m = date[_ + "Month"](),
|
||||
y = date[_ + "FullYear"](),
|
||||
H = date[_ + "Hours"](),
|
||||
M = date[_ + "Minutes"](),
|
||||
s = date[_ + "Seconds"](),
|
||||
L = date[_ + "Milliseconds"](),
|
||||
o = utc ? 0 : date.getTimezoneOffset(),
|
||||
flags = {
|
||||
d: d,
|
||||
dd: pad(d),
|
||||
ddd: AjaxIM.l10n.dayNames[D],
|
||||
dddd: AjaxIM.l10n.dayNames[D + 7],
|
||||
m: m + 1,
|
||||
mm: pad(m + 1),
|
||||
mmm: AjaxIM.l10n.monthNames[m],
|
||||
mmmm: AjaxIM.l10n.monthNames[m + 12],
|
||||
yy: String(y).slice(2),
|
||||
yyyy: y,
|
||||
h: H % 12 || 12,
|
||||
hh: pad(H % 12 || 12),
|
||||
H: H,
|
||||
HH: pad(H),
|
||||
M: M,
|
||||
MM: pad(M),
|
||||
s: s,
|
||||
ss: pad(s),
|
||||
l: pad(L, 3),
|
||||
L: pad(L > 99 ? Math.round(L / 10) : L),
|
||||
t: H < 12 ? "a" : "p",
|
||||
tt: H < 12 ? "am" : "pm",
|
||||
T: H < 12 ? "A" : "P",
|
||||
TT: H < 12 ? "AM" : "PM",
|
||||
Z: utc ? "UTC" :
|
||||
(String(date).match(timezone) || [""])
|
||||
.pop().replace(timezoneClip, ""),
|
||||
o: (o > 0 ? "-" : "+") +
|
||||
pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
|
||||
S: ["th", "st", "nd", "rd"][d % 10 > 3 ?
|
||||
0 :
|
||||
(d % 100 - d % 10 != 10) * d % 10]
|
||||
};
|
||||
|
||||
return mask.replace(token, function ($0) {
|
||||
return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
|
||||
});
|
||||
};
|
||||
}();
|
||||
|
||||
// Some common format strings
|
||||
dateFormat.masks = {
|
||||
"default": "ddd mmm dd yyyy HH:MM:ss",
|
||||
shortDate: "m/d/yy",
|
||||
mediumDate: "mmm d, yyyy",
|
||||
longDate: "mmmm d, yyyy",
|
||||
fullDate: "dddd, mmmm d, yyyy",
|
||||
shortTime: "h:MM TT",
|
||||
mediumTime: "h:MM:ss TT",
|
||||
longTime: "h:MM:ss TT Z",
|
||||
isoDate: "yyyy-mm-dd",
|
||||
isoTime: "HH:MM:ss",
|
||||
isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
|
||||
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
|
||||
};
|
||||
@@ -0,0 +1,913 @@
|
||||
AjaxIM = function(options) {
|
||||
if(this instanceof AjaxIM) {
|
||||
var self = this,
|
||||
_defaults = {
|
||||
host: 'localhost',
|
||||
port: 8000,
|
||||
theme: true
|
||||
};
|
||||
|
||||
this.settings = $.extend(_defaults, options);
|
||||
this.socket = new io.Socket(this.settings.host,
|
||||
{port: this.settings.port});
|
||||
|
||||
// Load & wire up the chat bar HTML
|
||||
var IM = $('<div id="AjaxIM"></div>')
|
||||
.appendTo('body')
|
||||
.css('display', 'none');
|
||||
|
||||
if(this.settings.theme) {
|
||||
if(typeof document.createStyleSheet == 'function')
|
||||
document.createStyleSheet('http://'
|
||||
+ this.settings.host
|
||||
+ ':' + this.settings.port
|
||||
+ '/theme.css');
|
||||
else
|
||||
$('body').append('<link rel="stylesheet" href="' +
|
||||
'http://'
|
||||
+ this.settings.host
|
||||
+ ':' + this.settings.port
|
||||
+ '/theme.css" />');
|
||||
}
|
||||
|
||||
this.friends = {};
|
||||
this.chats = {};
|
||||
this.username = '';
|
||||
this.user = store.get('user') || {
|
||||
identifier: '',
|
||||
activeTab: null,
|
||||
offline: false,
|
||||
status: ['online', '']
|
||||
};
|
||||
this._lastReconnect = 0;
|
||||
|
||||
// On window resize, check scroller visibility
|
||||
$(window).resize(function() {
|
||||
try {
|
||||
self._scrollers();
|
||||
} catch(e) {}
|
||||
});
|
||||
|
||||
this.socket.connect();
|
||||
this.socket.on('connect', function() { self._connected(); });
|
||||
this.socket.on('message', function(msg) { self._message(msg); });
|
||||
this.socket.on('disconnect', function() { self._disconnected(); });
|
||||
this.socket.on('connect_failed', function() { self._disconnected(); });
|
||||
} else {
|
||||
return AjaxIM.init(options);
|
||||
}
|
||||
};
|
||||
|
||||
AjaxIM.prototype._store = function(key, value) {
|
||||
if(['identifier', 'activeTab', 'offline', 'status']
|
||||
.indexOf(key) != -1) {
|
||||
this.user[key] = value;
|
||||
store.set('user', this.user);
|
||||
} else if(this.username.length) {
|
||||
store.set(key, Tea.encrypt(JSON.stringify(value), this.username));
|
||||
}
|
||||
};
|
||||
|
||||
AjaxIM.prototype._storeChat = function(username, content) {
|
||||
if(!this.chatstore) this.chatstore = {};
|
||||
|
||||
if(!(username in this.chatstore)) {
|
||||
this.chatstore[username] = [];
|
||||
} else if(this.chatstore[username].length > 300) {
|
||||
// If the chat store gets too long, it becomes slow to load.
|
||||
this.chatstore[username].shift();
|
||||
}
|
||||
|
||||
if(content && content.html.length) {
|
||||
if(content.replace_last)
|
||||
this.chatstore[username].pop();
|
||||
|
||||
this.chatstore[username].push(content.html);
|
||||
}
|
||||
|
||||
store.set('chats',
|
||||
Tea.encrypt(JSON.stringify(this.chatstore), this.username));
|
||||
};
|
||||
|
||||
AjaxIM.prototype._wiring = function() {
|
||||
var self = this;
|
||||
|
||||
// Prevent a chatbox from being minimized if just clicked
|
||||
$('.imjs-chatbox').live('click', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Allow a chatbox to be minimized via minimize button
|
||||
$('.imjs-chatbox .imjs-minimize').live('click', function() {
|
||||
$(this).parents('.imjs-selected').click();
|
||||
});
|
||||
|
||||
// Setup message sending for all chatboxes
|
||||
$('.imjs-chatbox .imjs-input')
|
||||
.live('keydown', function(event) {
|
||||
if(event.keyCode != 13 || ($.browser.msie && $.browser.version < 8))
|
||||
return;
|
||||
|
||||
var obj = $(this);
|
||||
self.send(obj.parents('.imjs-chatbox').data('username'), obj.val());
|
||||
})
|
||||
.live('keyup', function(event) {
|
||||
if(event.keyCode != 13)
|
||||
return;
|
||||
|
||||
if($.browser.msie && $.browser.version < 8) {
|
||||
var obj = $(this);
|
||||
self.send(obj.parents('.imjs-chatbox').data('username'),
|
||||
obj.val());
|
||||
}
|
||||
|
||||
$(this).val('').change();
|
||||
});
|
||||
|
||||
// Focus the text input when a chatbox message log is clicked
|
||||
$('.imjs-msglog').live('click', function() {
|
||||
var chatbox = $(this).parents('.imjs-chatbox');
|
||||
chatbox.find('.imjs-input').focus();
|
||||
});
|
||||
|
||||
// Create a chatbox when a buddylist item is clicked
|
||||
$('.imjs-friend').live('click', function() {
|
||||
var chatbox = self.createChatbox($(this).data('friend'));
|
||||
|
||||
if(chatbox.parents('.imjs-tab').data('state') != 'active') {
|
||||
chatbox.parents('.imjs-tab').click();
|
||||
self._store('activeTab', $(this).data('friend'));
|
||||
}
|
||||
|
||||
chatbox.find('.imjs-input').focus();
|
||||
});
|
||||
|
||||
// Setup the scrollers
|
||||
$('#imjs-scroll-right').live('click', function() {
|
||||
var hiddenTab = $(this)
|
||||
.prevAll('#imjs-bar li.imjs-tab:hidden')
|
||||
.filter(function() {
|
||||
return (
|
||||
$(this).data('state') != 'closed' &&
|
||||
$(this).prev('#imjs-bar li.imjs-tab:visible').length
|
||||
);
|
||||
})
|
||||
.slice(-1)
|
||||
.css('display', '');
|
||||
|
||||
if(hiddenTab.length) {
|
||||
$('#imjs-bar li.imjs-tab:visible').eq(0).css('display', 'none');
|
||||
$(this).html(parseInt($(this).html()) - 1);
|
||||
$('#imjs-scroll-left')
|
||||
.html(parseInt($('#imjs-scroll-left').html()) + 1);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
$('#imjs-scroll-left').live('click', function() {
|
||||
var hiddenTab = $(this)
|
||||
.nextAll('#imjs-bar li.imjs-tab:hidden')
|
||||
.filter(function() {
|
||||
return (
|
||||
$(this).data('state') != 'closed' &&
|
||||
$(this).next('#imjs-bar li.imjs-tab:visible').length
|
||||
);
|
||||
})
|
||||
.slice(-1)
|
||||
.css('display', '');
|
||||
|
||||
if(hiddenTab.length) {
|
||||
$('#imjs-bar li.imjs-tab:visible').slice(-1).css('display', 'none');
|
||||
$(this).html(parseInt($(this).html()) - 1);
|
||||
$('#imjs-scroll-right')
|
||||
.html(parseInt($('#imjs-scroll-right').html()) + 1);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Setup status buttons
|
||||
$('#imjs-status-panel .imjs-button').live('click', function() {
|
||||
var status = this.id.split('-')[2],
|
||||
away_msg = $('#imjs-away-message-text');
|
||||
|
||||
$('#imjs-away-message-text, #imjs-away-message-text-arrow').animate({
|
||||
opacity: (status == 'away' ? 'show' : 'hide'),
|
||||
height: (status == 'away' ? 'show' : 'hide')
|
||||
}, 50);
|
||||
|
||||
$('#imjs-status-panel .imjs-button').removeClass('imjs-toggled');
|
||||
$(this).addClass('imjs-toggled');
|
||||
|
||||
if(self.user.status[0] == 'away')
|
||||
self._last_status_message = away_msg.val();
|
||||
|
||||
away_msg.val(status == 'away'
|
||||
? self._last_status_message ||
|
||||
AjaxIM.l10n.defaultAway
|
||||
: '');
|
||||
|
||||
self.status(status, away_msg.val());
|
||||
return false;
|
||||
});
|
||||
|
||||
// Allow status message to be changed
|
||||
$('#imjs-away-message-text')
|
||||
.live('keyup', (function() {
|
||||
var msg_type_timer = null;
|
||||
|
||||
return function() {
|
||||
if(msg_type_timer) clearTimeout(msg_type_timer);
|
||||
|
||||
msg_type_timer = setTimeout(function() {
|
||||
self._last_status_message =
|
||||
self.current_status[1] = $('#imjs-away-message-text')
|
||||
.addClass('imjs-loading').val();
|
||||
self.status.apply(self, self.current_status);
|
||||
}, 250);
|
||||
};
|
||||
})());
|
||||
$(this).bind('statusChanged', function() {
|
||||
$('#imjs-away-message-text').removeClass('imjs-loading');
|
||||
});
|
||||
|
||||
// Setup reconnect button
|
||||
$('#imjs-reconnect').live('click', function() {
|
||||
$('#imjs-reconnect').hide();
|
||||
$('.imjs-input').attr('disabled', false);
|
||||
|
||||
self.socket.connect();
|
||||
});
|
||||
|
||||
// Allow tabs to be activated and closed
|
||||
$('.imjs-tab')
|
||||
.live('click', function() {
|
||||
return self.activateTab.call(self, $(this));
|
||||
});
|
||||
|
||||
$('.imjs-tab .imjs-close')
|
||||
.live('click', function() {
|
||||
return self.closeTab.call(self, $(this));
|
||||
});
|
||||
|
||||
// Set up the friends list actions
|
||||
$(document).click(function(e) {
|
||||
if(~['imjs-friends'].indexOf(e.target.id) ||
|
||||
$(e.target).parents('#imjs-friends').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if($('#imjs-friends').data('state') == 'active')
|
||||
self.activateTab.call(self, $('#imjs-friends'));
|
||||
else if($('#imjs-status').data('state') == 'active')
|
||||
self.activateTab.call(self, $('#imjs-status'));
|
||||
});
|
||||
|
||||
$('#imjs-friends')
|
||||
.live('click', function(e) {
|
||||
if(!$(this).hasClass('imjs-not-connected') &&
|
||||
e.target.id != 'imjs-friends-panel' &&
|
||||
!$(e.target).parents('#imjs-friends-panel').length)
|
||||
self.activateTab.call(self, $(this));
|
||||
})
|
||||
/*
|
||||
.mouseenter(function() {
|
||||
if($(this).hasClass('imjs-not-connected')) {
|
||||
$('.imjs-tooltip')
|
||||
.css('display', 'block')
|
||||
.find('p')
|
||||
.html(AjaxIM.l10n.notConnectedTip);
|
||||
|
||||
var tip_left = $(this).offset().left -
|
||||
$('.imjs-tooltip').outerWidth() +
|
||||
($(this).outerWidth() / 2);
|
||||
var tip_top = $(this).offset().top -
|
||||
$('.imjs-tooltip').outerHeight(true);
|
||||
|
||||
$('.imjs-tooltip').css({
|
||||
left: tip_left,
|
||||
top: tip_top
|
||||
});
|
||||
}
|
||||
})
|
||||
.mouseleave(function() {
|
||||
if($(this).hasClass('imjs-not-connected')) {
|
||||
$('.imjs-tooltip').css('display', '');
|
||||
}
|
||||
});
|
||||
*/
|
||||
};
|
||||
|
||||
AjaxIM.prototype._setup = function() {
|
||||
$('.imjs-scroll, #imjs-friends-panel').css('display', 'none');
|
||||
$('#imjs-friends').data('state', 'minimized');
|
||||
};
|
||||
|
||||
AjaxIM.prototype._restore = function() {
|
||||
var self = this,
|
||||
chatstore = Tea.decrypt(store.get('chats') || '', this.username),
|
||||
friends = Tea.decrypt(store.get('friends') || '', this.username);
|
||||
|
||||
try {
|
||||
this.chatstore = JSON.parse(chatstore);
|
||||
} catch(e) {
|
||||
this.chatstore = {};
|
||||
store.set('chats', '');
|
||||
}
|
||||
|
||||
try {
|
||||
friends = JSON.parse(friends);
|
||||
} catch(e) {
|
||||
friends = {};
|
||||
store.set('friends', '');
|
||||
}
|
||||
|
||||
if(friends) {
|
||||
$.each(friends, function(friend, info) {
|
||||
self.addFriend(friend, info.status || 'offline', info.group || 'Friends');
|
||||
});
|
||||
|
||||
$('#imjs-friends').removeClass('imjs-not-connected')
|
||||
.addClass('imjs-' + this.user.status[0]);
|
||||
|
||||
$('#imjs-button-' + status[0]).addClass('imjs-toggled');
|
||||
if(this.user.status[0] == 'away') {
|
||||
setTimeout(function() {
|
||||
$('#imjs-away-message-text, #imjs-away-message-text-arrow')
|
||||
.show();
|
||||
}, 250);
|
||||
$('#imjs-away-message-text').val(this.user.status[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$.each(this.chatstore, function(username, convo) {
|
||||
if(!convo.length) return;
|
||||
|
||||
if(typeof convo == 'string')
|
||||
convo = self.chatstore[username] = JSON.parse(convo);
|
||||
|
||||
var chatbox = self.createChatbox(username, convo.join(''));
|
||||
chatbox.data('lastDateStamp', null).css('display', 'none');
|
||||
|
||||
$(self).trigger('chatRestored', [username, chatbox]);
|
||||
});
|
||||
|
||||
if(this.user.activeTab && this.user.activeTab in this.chats) {
|
||||
this.chats[this.user.activeTab].parents('.imjs-tab').click();
|
||||
var msglog = this.chats[this.user.activeTab].find('.imjs-msglog');
|
||||
msglog[0].scrollTop = msglog[0].scrollHeight;
|
||||
}
|
||||
|
||||
// Set username in Friends list
|
||||
var header = $('#imjs-friends-panel .imjs-header');
|
||||
header.html(header.html().replace('{username}', this.username));
|
||||
};
|
||||
|
||||
AjaxIM.prototype._kill = function() {
|
||||
var last_user = store.get('user');
|
||||
$.each(['friends', 'activeTab', 'chats', 'status', 'connected'],
|
||||
function(i, key) {
|
||||
store.remove(last_user + '-' + key);
|
||||
});
|
||||
store.set('user', '');
|
||||
|
||||
this.chats = {};
|
||||
this.friends = {};
|
||||
this.chatstore = {};
|
||||
|
||||
$('.imjs-friend-group').remove();
|
||||
|
||||
delete this.user;
|
||||
};
|
||||
|
||||
AjaxIM.prototype._connected = function() {
|
||||
this.socket.send({
|
||||
type: 'AUTH',
|
||||
cookies: document.cookie,
|
||||
identifier: this.user.identifier.length
|
||||
? this.user.identifier
|
||||
: ''
|
||||
});
|
||||
};
|
||||
|
||||
AjaxIM.prototype._message = function(msg) {
|
||||
var self = this;
|
||||
_dbg(msg);
|
||||
switch(msg.type) {
|
||||
case 'AUTH':
|
||||
if(msg.loggedin) {
|
||||
if('identifier' in msg) {
|
||||
this._store('identifier', msg.identifier);
|
||||
}
|
||||
|
||||
this.username = msg.username;
|
||||
|
||||
$('#AjaxIM').html('').append($(Template.bar)).show();
|
||||
this._setup();
|
||||
|
||||
// a run-once-per-page-load function
|
||||
this._wiring();
|
||||
this._wiring = function(){};
|
||||
|
||||
if(this.user.offline == true) {
|
||||
var self = this;
|
||||
this.socket.disconnect();
|
||||
setTimeout(function() { self._showReconnect(); }, 0);
|
||||
return;
|
||||
} else {
|
||||
this._restore();
|
||||
}
|
||||
|
||||
$('#imjs-friends').attr('class', 'imjs-online');
|
||||
|
||||
if(msg.friends) {
|
||||
$.each(msg.friends, function(friend, status) {
|
||||
self.addFriend(friend, status, 'Friends');
|
||||
});
|
||||
this._store('friends', this.friends);
|
||||
}
|
||||
|
||||
// Set username in Friends list
|
||||
var header = $('#imjs-friends-panel .imjs-header');
|
||||
header.html(header.html().replace('{username}', this.username));
|
||||
|
||||
// Set status available
|
||||
$('#imjs-away-message-text, #imjs-away-message-text-arrow').hide();
|
||||
$('#imjs-status-panel .imjs-button').removeClass('imjs-toggled');
|
||||
$('#imjs-button-online').addClass('imjs-toggled');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'IM':
|
||||
if(!msg['from']) return;
|
||||
|
||||
if(!this.chats[msg.from])
|
||||
this.createChatbox(msg.from);
|
||||
|
||||
this._storeChat(
|
||||
msg.from,
|
||||
this._addMessage('them', this.chats[msg.from], msg.from, msg.message)
|
||||
);
|
||||
break;
|
||||
|
||||
case 'STATUS':
|
||||
if(!msg['username']) return;
|
||||
|
||||
this._friendUpdate(msg.username, msg.status);
|
||||
this._store('friends', this.friends);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
AjaxIM.prototype._disconnected = function() {
|
||||
$('#imjs-friends')
|
||||
.addClass('imjs-not-connected');
|
||||
if($('#imjs-friends').hasClass('imjs-selected'))
|
||||
this.activateTab($('#imjs-friends'));
|
||||
$('.imjs-input').attr('disabled', true);
|
||||
$('#imjs-reconnect').show();
|
||||
};
|
||||
|
||||
AjaxIM.prototype.friendsListNoop = function() {
|
||||
if($(this).hasClass('imjs-not-connected'))
|
||||
return false;
|
||||
};
|
||||
|
||||
AjaxIM.prototype.send = function(username, message) {
|
||||
if(!message) return;
|
||||
var self = this;
|
||||
|
||||
if(this.chats[username]) {
|
||||
// possibly add a datestamp
|
||||
this._storeChat(username, this._addDateStamp(this.chats[username]));
|
||||
this._storeChat(username,
|
||||
this._addMessage('you', this.chats[username],
|
||||
this.username, message));
|
||||
}
|
||||
|
||||
this.socket.send({
|
||||
type: 'IM',
|
||||
to: username,
|
||||
message: message
|
||||
});
|
||||
|
||||
$(this).trigger('messageSent', [username, message]);
|
||||
};
|
||||
|
||||
AjaxIM.prototype._statuses = ['offline', 'online', 'away'];
|
||||
AjaxIM.prototype.status = function(value, message) {
|
||||
var self = this;
|
||||
|
||||
// update status icon(s)
|
||||
if(this._statuses.indexOf(value) == -1)
|
||||
return;
|
||||
|
||||
// check if selected before writing over the class!
|
||||
$(this._statuses).each(function() {
|
||||
$('#imjs-friends').removeClass('imjs-' + this);
|
||||
});
|
||||
$('#imjs-friends').addClass('imjs-' + value);
|
||||
|
||||
this.socket.send({
|
||||
type: 'STATUS',
|
||||
status: value,
|
||||
status_msg: message
|
||||
});
|
||||
|
||||
$(this).trigger('statusChanged', [value, message]);
|
||||
};
|
||||
|
||||
AjaxIM.prototype.addFriend = function(username, status, group) {
|
||||
var group_id = 'imjs-group-' + md5.hex(group);
|
||||
|
||||
if(!(group_item = $('#' + group_id)).length) {
|
||||
var group_item = $(Template.group)
|
||||
.attr('id', group_id)
|
||||
.data('group', group)
|
||||
.appendTo('#imjs-friends-list');
|
||||
|
||||
var group_header = group_item.find('.imjs-friend-group-header');
|
||||
group_header.html(group_header.html().replace('{group}', group));
|
||||
}
|
||||
|
||||
var user_id = 'imjs-friend-' + md5.hex(username + group);
|
||||
|
||||
if(!$('#' + user_id).length) {
|
||||
var user_item = $(Template.friend)
|
||||
.addClass('imjs-' + status)
|
||||
.attr('id', user_id)
|
||||
.data('friend', username)
|
||||
.appendTo(group_item.find('ul'));
|
||||
if(status == 'offline')
|
||||
user_item.hide();
|
||||
user_item.html(
|
||||
user_item.html()
|
||||
.replace('{username}', username)
|
||||
.replace('{status}', status)
|
||||
);
|
||||
user_item.find('.imjs-friend-status')
|
||||
.attr('title', status);
|
||||
}
|
||||
|
||||
this.friends[username] = {status: status, group: group};
|
||||
this._updateFriendCount();
|
||||
return this.friends[username];
|
||||
};
|
||||
|
||||
AjaxIM.prototype._updateFriendCount = function() {
|
||||
var friendsLength = 0;
|
||||
$.each(this.friends, function(u, f) {
|
||||
if(f.status != 'offline') friendsLength++;
|
||||
});
|
||||
$('#imjs-friends .imjs-tab-text span span').html(friendsLength);
|
||||
};
|
||||
|
||||
AjaxIM.prototype._friendUpdate = function(username, status, statusMessage) {
|
||||
if(this.chats[username]) {
|
||||
var tab = this.chats[username].parents('.imjs-tab');
|
||||
var tab_class = 'imjs-tab';
|
||||
if(tab.data('state') == 'active') tab_class += ' imjs-selected';
|
||||
tab_class += ' imjs-' + status;
|
||||
|
||||
tab.attr('class', tab_class);
|
||||
|
||||
// display the status in the chatbox
|
||||
var date_stamp =
|
||||
$('.imjs-tab.imjs-default .imjs-chatbox .imjs-msglog .imjs-date').clone();
|
||||
|
||||
var date_stamp_time = date_stamp.find('.imjs-msg-time');
|
||||
if(date_stamp_time.length)
|
||||
date_stamp_time.html(dateFormat(date_stamp_time.html()));
|
||||
|
||||
var date_stamp_date = date_stamp.find('.imjs-date-date').html(
|
||||
AjaxIM.l10n[
|
||||
'chat' + status.slice(0, 1).toUpperCase() + status.slice(1)
|
||||
].replace(/%s/g, username));
|
||||
|
||||
var msglog = this.chats[username].find('.imjs-msglog');
|
||||
date_stamp.appendTo(msglog);
|
||||
msglog[0].scrollTop = msglog[0].scrollHeight;
|
||||
}
|
||||
|
||||
if(this.friends[username]) {
|
||||
var friend_id = 'imjs-friend-' + md5.hex(username + 'Friends');
|
||||
$('#' + friend_id).attr('class', 'imjs-friend imjs-' + status);
|
||||
$('#' + friend_id).find('.imjs-friend-status')
|
||||
.html(statusMessage);
|
||||
|
||||
if(status == 'offline') {
|
||||
$('#' + friend_id + ':visible').slideUp();
|
||||
$('#' + friend_id + ':hidden').hide();
|
||||
} else if(!$('#' + friend_id + ':visible').length) {
|
||||
$('#' + friend_id).slideDown();
|
||||
}
|
||||
|
||||
this.friends[username].status = status;
|
||||
this._updateFriendCount();
|
||||
}
|
||||
};
|
||||
|
||||
AjaxIM.prototype.addTab = function(label, action, closable) {
|
||||
var tab = $(Template.tab).insertAfter('#imjs-scroll-left');
|
||||
tab.attr('id', 'imjs-tab-' + md5.hex(label))
|
||||
.html(tab.html().replace('{label}', label))
|
||||
.data('state', 'minimized');
|
||||
|
||||
var notification = tab.find('.imjs-notification');
|
||||
notification.css('display', 'none')
|
||||
.data('count', 0)
|
||||
.data('default-text', notification.html())
|
||||
.html(notification.html().replace('{count}', '0'));
|
||||
|
||||
if(closable === false)
|
||||
tab.find('.imjs-close').eq(0).remove();
|
||||
|
||||
if(typeof action != 'string') {
|
||||
tab.find('.imjs-chatbox').remove();
|
||||
tab.click(action);
|
||||
}
|
||||
|
||||
return tab;
|
||||
};
|
||||
|
||||
AjaxIM.prototype.activateTab = function(tab) {
|
||||
var chatbox = tab.find('.imjs-chatbox') || false,
|
||||
input;
|
||||
|
||||
if(tab.data('state') != 'active') {
|
||||
if(tab.attr('id') != 'imjs-friends') {
|
||||
$('#imjs-bar > li')
|
||||
.not(tab)
|
||||
.not('#imjs-friends, .imjs-scroll, .imjs-default')
|
||||
.add(tab.attr('id') == 'imjs-status' ? '#imjs-friends' : '')
|
||||
.removeClass('imjs-selected')
|
||||
.each(function() {
|
||||
var self = $(this);
|
||||
if(self.data('state') != 'closed') {
|
||||
self.data('state', 'minimized');
|
||||
var chatbox = self.find('.imjs-chatbox');
|
||||
if(chatbox.length)
|
||||
chatbox.css('display', 'none');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('#imjs-status')
|
||||
.removeClass('imjs-selected')
|
||||
.data('state', 'minimized')
|
||||
.find('.imjs-chatbox')
|
||||
.css('display', 'none');
|
||||
}
|
||||
|
||||
if(chatbox && chatbox.css('display') == 'none')
|
||||
chatbox.css('display', '');
|
||||
|
||||
// set the tab to active...
|
||||
tab.addClass('imjs-selected').data('state', 'active');
|
||||
|
||||
// ...and hide and reset the notification icon
|
||||
tab.find('.imjs-notification').css('display', 'none')
|
||||
.data('count', 0);
|
||||
|
||||
if(chatbox && (username = chatbox.data('username')))
|
||||
this._store('activeTab', username);
|
||||
|
||||
$(this).trigger('tabToggled', ['activated', tab]);
|
||||
} else {
|
||||
tab.removeClass('imjs-selected').data('state', 'minimized');
|
||||
|
||||
if(chatbox && chatbox.css('display') != 'none')
|
||||
chatbox.css('display', 'none');
|
||||
|
||||
this._store('activeTab', username);
|
||||
|
||||
$(this).trigger('tabToggled', ['minimized', tab]);
|
||||
}
|
||||
|
||||
if(chatbox) {
|
||||
try {
|
||||
var msglog = chatbox.find('.imjs-msglog');
|
||||
msglog[0].scrollTop = msglog[0].scrollHeight;
|
||||
} catch(e) {}
|
||||
|
||||
try { chatbox.find('.imjs-input').focus(); } catch(e) {}
|
||||
}
|
||||
};
|
||||
|
||||
AjaxIM.prototype.closeTab = function(tab) {
|
||||
tab = tab.parents('.imjs-tab');
|
||||
tab.css('display', 'none')
|
||||
.removeClass('imjs-selected')
|
||||
.data('state', 'closed');
|
||||
|
||||
delete this.chatstore[tab.find('.imjs-chatbox').data('username')];
|
||||
store.set('chats',
|
||||
Tea.encrypt(JSON.stringify(this.chatstore), this.username));
|
||||
|
||||
$(this).trigger('tabToggled', ['closed', tab]);
|
||||
|
||||
this._scrollers();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
AjaxIM.prototype.createChatbox = function(username, contents) {
|
||||
var self = this,
|
||||
chatbox_id = 'imjs-' + md5.hex(username);
|
||||
if(!(chatbox = $('#' + chatbox_id)).length) {
|
||||
// add a tab
|
||||
var tab = this.addTab(username, '#' + chatbox_id);
|
||||
var chatbox = tab.find('.imjs-chatbox');
|
||||
|
||||
chatbox.attr('id', chatbox_id);
|
||||
|
||||
// setup the textarea to autogrow
|
||||
chatbox.find('.imjs-input').autogrow();
|
||||
|
||||
// remove default items from the message log
|
||||
var message_log = chatbox.find('.imjs-msglog').empty();
|
||||
|
||||
// setup the chatbox header
|
||||
var cb_header = chatbox.find('.imjs-header');
|
||||
cb_header.html(cb_header.html().replace('{username}', username));
|
||||
|
||||
if(!contents) {
|
||||
// add a date stamp
|
||||
this._storeChat(username, this._addDateStamp(chatbox));
|
||||
} else {
|
||||
message_log.html(contents);
|
||||
}
|
||||
|
||||
// associate the username with the object and vice-versa
|
||||
this.chats[username] = chatbox;
|
||||
chatbox.data('username', username);
|
||||
|
||||
if(username in this.friends) {
|
||||
status = this.friends[username].status;
|
||||
tab.addClass('imjs-' + status);
|
||||
}
|
||||
|
||||
setTimeout(function() { self._scrollers(); }, 0);
|
||||
} else if(chatbox.parents('.imjs-tab').data('state') == 'closed') {
|
||||
chatbox.find('.imjs-msglog > *').addClass('imjs-msg-old');
|
||||
|
||||
var tab = chatbox.parents('.imjs-tab');
|
||||
if(tab.css('display') == 'none')
|
||||
tab.css('display', '').removeClass('imjs-selected')
|
||||
.insertAfter('#imjs-scroll-left')
|
||||
.data('state', 'minimized');
|
||||
|
||||
if(!no_stamp) {
|
||||
// possibly add a date stamp
|
||||
this._storeChat(username, this._addDateStamp(chatbox));
|
||||
}
|
||||
|
||||
if(!$('#imjs-bar .imjs-selected').length) {
|
||||
tab.click();
|
||||
} else {
|
||||
this.notification(tab);
|
||||
}
|
||||
|
||||
setTimeout(function() { self._scrollers() }, 0);
|
||||
}
|
||||
|
||||
return chatbox;
|
||||
};
|
||||
|
||||
AjaxIM.prototype._addDateStamp = function(chatbox, time) {
|
||||
var message_log = $(chatbox).find('.imjs-msglog');
|
||||
if(!time)
|
||||
time = (new Date()).getTime();
|
||||
|
||||
var date_stamp = $(Template.datestamp);
|
||||
var date_stamp_time = date_stamp.find('.imjs-msg-time');
|
||||
if(date_stamp_time.length)
|
||||
date_stamp_time.html(dateFormat(time, date_stamp_time.html()));
|
||||
|
||||
var date_stamp_date = date_stamp.find('.imjs-date-date');
|
||||
var formatted_date = dateFormat(time, date_stamp_date.html());
|
||||
if(chatbox.data('lastDateStamp') != formatted_date) {
|
||||
if(date_stamp_date.length)
|
||||
date_stamp_date.html(dateFormat(time, date_stamp_date.html()));
|
||||
|
||||
chatbox.data('lastDateStamp', formatted_date);
|
||||
date_stamp.appendTo(message_log);
|
||||
|
||||
return {
|
||||
replace_last: false,
|
||||
html: jQuery('<div>').append(date_stamp.clone()).html()
|
||||
};
|
||||
} else {
|
||||
return {replace_last: false, html: ''};
|
||||
}
|
||||
};
|
||||
|
||||
AjaxIM.prototype._addMessage = function(yt, chatbox, username, message, time) {
|
||||
var message_container = chatbox.find('.imjs-msglog > *:last-child'),
|
||||
new_container = false;
|
||||
if(!message_container.hasClass('imjs-msg-' + yt + '-container')) {
|
||||
// message isn't from same person; create new message container
|
||||
message_container =
|
||||
$(Template['message_' + yt + '_outer']
|
||||
.replace('{username}', username))
|
||||
.appendTo(chatbox.find('.imjs-msglog'));
|
||||
new_container = true;
|
||||
}
|
||||
|
||||
// clean up the message
|
||||
message = message.toString().replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/(^|.*)\*([^*]+)\*(.*|$)/, '$1<strong>$2</strong>$3');
|
||||
|
||||
// autolink URLs
|
||||
message = message.replace(
|
||||
new RegExp('([A-Za-z][A-Za-z0-9+.-]{1,120}:[A-Za-z0-9/]' +
|
||||
'(([A-Za-z0-9$_.+!*,;/?:@&~=-])|%[A-Fa-f0-9]{2}){1,333}' +
|
||||
'(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*,;/?:@&~=%-]{0,1000}))?)', 'g'),
|
||||
'<a href="$1" target="_blank">$1</a>');
|
||||
|
||||
// insert the message
|
||||
var single_message = $(
|
||||
Template['message_' + yt].replace('{message}', message)
|
||||
).appendTo(message_container.find('ul'));
|
||||
|
||||
// set the message time
|
||||
var msgtime = single_message.find('.imjs-msg-time');
|
||||
if(!time)
|
||||
time = new Date();
|
||||
|
||||
if(typeof time != 'string')
|
||||
time = dateFormat(time, msgtime.html());
|
||||
|
||||
msgtime.html(time);
|
||||
|
||||
var msglog = chatbox.find('.imjs-msglog');
|
||||
msglog[0].scrollTop = msglog[0].scrollHeight;
|
||||
|
||||
return {
|
||||
replace_last: !new_container,
|
||||
html: jQuery('<div>').append(message_container.clone()).html()
|
||||
};
|
||||
};
|
||||
|
||||
AjaxIM.prototype._scrollers = function() {
|
||||
var needScrollers = false;
|
||||
$('#imjs-scroll-left').nextAll('.imjs-tab')
|
||||
.filter(function() {
|
||||
return $(this).data('state') != 'closed';
|
||||
})
|
||||
.each(function(i, tab) {
|
||||
tab = $(tab).css('display', '');
|
||||
|
||||
var tab_pos = tab.position();
|
||||
if(tab_pos.top >= $('#imjs-bar').height() ||
|
||||
tab_pos.left < 0 ||
|
||||
tab_pos.right > $(document).width()) {
|
||||
$('.imjs-scroll').css('display', '');
|
||||
tab.css('display', 'none');
|
||||
needScrollers = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(!needScrollers) {
|
||||
$('.imjs-scroll').css('display', 'none');
|
||||
}
|
||||
|
||||
if($('#imjs-scroll-left').css('display') != 'none' &&
|
||||
$('#imjs-scroll-right').position().top >= $('#imjs-bar').height()) {
|
||||
$('#imjs-bar li.imjs-tab:visible').slice(-1).css('display', 'none');
|
||||
}
|
||||
|
||||
if($('#imjs-bar li.imjs-tab:visible').length) {
|
||||
while($('.imjs-selected').css('display') == 'none')
|
||||
$('#imjs-scroll-right').click();
|
||||
}
|
||||
|
||||
var hiddenRight = $('#imjs-bar li.imjs-tab:visible').slice(-1)
|
||||
.nextAll('#imjs-bar li.imjs-tab:hidden')
|
||||
.not('.imjs-default')
|
||||
.filter(function() {
|
||||
return $(this).data('state') != 'closed'
|
||||
}).length;
|
||||
|
||||
var hiddenLeft = $('#imjs-bar li.imjs-tab:visible').eq(0)
|
||||
.prevAll('#imjs-bar li.imjs-tab:hidden')
|
||||
.not('.imjs-default')
|
||||
.filter(function() {
|
||||
return $(this).data('state') != 'closed'
|
||||
}).length;
|
||||
|
||||
$('#imjs-scroll-left').html(hiddenLeft);
|
||||
$('#imjs-scroll-right').html(hiddenRight);
|
||||
};
|
||||
|
||||
AjaxIM.init = function(options) {
|
||||
if(!AjaxIM.client)
|
||||
AjaxIM.client = new AjaxIM(options);
|
||||
|
||||
return AjaxIM.client;
|
||||
};
|
||||
|
||||
AjaxIM.debug = true;
|
||||
function _dbg(msg) {
|
||||
if(AjaxIM.debug && window.console) console.log(msg);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* Ajax IM (AJAX Instant Messenger)
|
||||
* http://ajaxim.com/ | http://github.com/endtwist/AjaxIM
|
||||
* (c) 2005-2011, Joshua Gross
|
||||
* MIT license
|
||||
*
|
||||
* Includes:
|
||||
* JavaScript implementation of the RSA Data Security, Inc. MD5 Message 2.1
|
||||
* http://pajhome.org.uk/crypt/md5
|
||||
* (c) 2002 Paul Johnston
|
||||
* BSD License
|
||||
*
|
||||
* Date Format 1.2.3
|
||||
* http://blog.stevenlevithan.com/archives/date-time-format
|
||||
* (c) 2009 Steven Levithan
|
||||
* MIT license
|
||||
*
|
||||
* Block TEA (xxtea) Tiny Encryption Algorithm, Base64 class, Utf8 class
|
||||
* www.movable-type.co.uk/tea-block.html
|
||||
* (c) 2002-2010 Chris Veness
|
||||
* CC Attribution
|
||||
*
|
||||
* store.js
|
||||
* https://github.com/marcuswestin/store.js
|
||||
* (c) 2010-2011 Marcus Westin
|
||||
* MIT license
|
||||
*
|
||||
*/
|
||||
(function($) {
|
||||
@@ -0,0 +1,152 @@
|
||||
/*!
|
||||
* jQuery JavaScript Library v1.4.1
|
||||
* http://jquery.com/
|
||||
*
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* http://sizzlejs.com/
|
||||
* Copyright 2010, The Dojo Foundation
|
||||
* Released under the MIT, BSD, and GPL Licenses.
|
||||
*
|
||||
* Date: Mon Jan 25 19:43:33 2010 -0500
|
||||
*/
|
||||
(function(z,v){function la(){if(!c.isReady){try{r.documentElement.doScroll("left")}catch(a){setTimeout(la,1);return}c.ready()}}function Ma(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var n in b)X(a,n,b[n],f,e,d);return a}if(d!==v){f=!i&&f&&c.isFunction(d);for(n=0;n<j;n++)e(a[n],b,f?d.call(a[n],n,e(a[n],b)):d,i);return a}return j?
|
||||
e(a[0],b):null}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function ma(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function na(a){var b,d=[],f=[],e=arguments,i,j,n,o,m,s,x=c.extend({},c.data(this,"events").live);if(!(a.button&&a.type==="click")){for(o in x){j=x[o];if(j.live===a.type||j.altLive&&c.inArray(a.type,j.altLive)>-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete x[o]}i=c(a.target).closest(f,
|
||||
a.currentTarget);m=0;for(s=i.length;m<s;m++)for(o in x){j=x[o];n=i[m].elem;f=null;if(i[m].selector===j.selector){if(j.live==="mouseenter"||j.live==="mouseleave")f=c(a.relatedTarget).closest(j.selector)[0];if(!f||f!==n)d.push({elem:n,fn:j})}}m=0;for(s=d.length;m<s;m++){i=d[m];a.currentTarget=i.elem;a.data=i.fn.data;if(i.fn.apply(i.elem,e)===false){b=false;break}}return b}}function oa(a,b){return"live."+(a?a+".":"")+b.replace(/\./g,"`").replace(/ /g,"&")}function pa(a){return!a||!a.parentNode||a.parentNode.nodeType===
|
||||
11}function qa(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var i in f)for(var j in f[i])c.event.add(this,i,f[i][j],f[i][j].data)}}})}function ra(a,b,d){var f,e,i;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&a[0].indexOf("<option")<0&&(c.support.checkClone||!sa.test(a[0]))){e=true;if(i=c.fragments[a[0]])if(i!==1)f=i}if(!f){b=b&&b[0]?b[0].ownerDocument||b[0]:r;f=b.createDocumentFragment();
|
||||
c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=i?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(ta.concat.apply([],ta.slice(0,b)),function(){d[this]=a});return d}function ua(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Na=z.jQuery,Oa=z.$,r=z.document,S,Pa=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Qa=/^.[^:#\[\.,]*$/,Ra=/\S/,Sa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Ta=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,O=navigator.userAgent,
|
||||
va=false,P=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,Q=Array.prototype.slice,wa=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Pa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:r;if(a=Ta.exec(a))if(c.isPlainObject(b)){a=[r.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ra([d[1]],
|
||||
[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=r.getElementById(d[2])){if(b.id!==d[2])return S.find(a);this.length=1;this[0]=b}this.context=r;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=r;a=r.getElementsByTagName(a)}else return!b||b.jquery?(b||S).find(a):c(b).find(a);else if(c.isFunction(a))return S.ready(a);if(a.selector!==v){this.selector=a.selector;this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a,
|
||||
this)},selector:"",jquery:"1.4.1",length:0,size:function(){return this.length},toArray:function(){return Q.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=0;ba.apply(this,a);return this},each:function(a,b){return c.each(this,
|
||||
a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(r,c);else P&&P.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Q.apply(this,arguments),"slice",Q.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};
|
||||
c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,n;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(i in e){j=a[i];n=e[i];if(a!==n)if(f&&n&&(c.isPlainObject(n)||c.isArray(n))){j=j&&(c.isPlainObject(j)||c.isArray(j))?j:c.isArray(n)?[]:{};a[i]=c.extend(f,j,n)}else if(n!==v)a[i]=n}return a};c.extend({noConflict:function(a){z.$=
|
||||
Oa;if(a)z.jQuery=Na;return c},isReady:false,ready:function(){if(!c.isReady){if(!r.body)return setTimeout(c.ready,13);c.isReady=true;if(P){for(var a,b=0;a=P[b++];)a.call(r,c);P=null}c.fn.triggerHandler&&c(r).triggerHandler("ready")}},bindReady:function(){if(!va){va=true;if(r.readyState==="complete")return c.ready();if(r.addEventListener){r.addEventListener("DOMContentLoaded",L,false);z.addEventListener("load",c.ready,false)}else if(r.attachEvent){r.attachEvent("onreadystatechange",L);z.attachEvent("onload",
|
||||
c.ready);var a=false;try{a=z.frameElement==null}catch(b){}r.documentElement.doScroll&&a&&la()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,"isPrototypeOf"))return false;var b;for(b in a);return b===v||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;
|
||||
return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return z.JSON&&z.JSON.parse?z.JSON.parse(a):(new Function("return "+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Ra.test(a)){var b=r.getElementsByTagName("head")[0]||
|
||||
r.documentElement,d=r.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(r.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,i=a.length,j=i===v||c.isFunction(a);if(d)if(j)for(f in a){if(b.apply(a[f],d)===false)break}else for(;e<i;){if(b.apply(a[e++],d)===false)break}else if(j)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=
|
||||
a[0];e<i&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Sa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==
|
||||
v;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,i=a.length;e<i;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,i=0,j=a.length;i<j;i++){e=b(a[i],i,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=v}else if(b&&!c.isFunction(b)){d=b;b=v}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},
|
||||
uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});O=c.uaMatch(O);if(O.browser){c.browser[O.browser]=true;c.browser.version=O.version}if(c.browser.webkit)c.browser.safari=true;if(wa)c.inArray=function(a,b){return wa.call(b,a)};S=c(r);if(r.addEventListener)L=function(){r.removeEventListener("DOMContentLoaded",
|
||||
L,false);c.ready()};else if(r.attachEvent)L=function(){if(r.readyState==="complete"){r.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=r.documentElement,b=r.createElement("script"),d=r.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support=
|
||||
{leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:r.createElement("select").appendChild(r.createElement("option")).selected,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};
|
||||
b.type="text/javascript";try{b.appendChild(r.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(z[f]){c.support.scriptEval=true;delete z[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function n(){c.support.noCloneEvent=false;d.detachEvent("onclick",n)});d.cloneNode(true).fireEvent("onclick")}d=r.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=r.createDocumentFragment();a.appendChild(d.firstChild);
|
||||
c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var n=r.createElement("div");n.style.width=n.style.paddingLeft="1px";r.body.appendChild(n);c.boxModel=c.support.boxModel=n.offsetWidth===2;r.body.removeChild(n).style.display="none"});a=function(n){var o=r.createElement("div");n="on"+n;var m=n in o;if(!m){o.setAttribute(n,"return;");m=typeof o[n]==="function"}return m};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props=
|
||||
{"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ua=0,xa={},Va={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var f=a[G],e=c.cache;if(!b&&!f)return null;f||(f=++Ua);if(typeof b==="object"){a[G]=f;e=e[f]=c.extend(true,
|
||||
{},b)}else e=e[f]?e[f]:typeof d==="undefined"?Va:(e[f]={});if(d!==v){a[G]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[G]}catch(i){a.removeAttribute&&a.removeAttribute(G)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,
|
||||
a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===v){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===v&&this.length)f=c.data(this[0],a);return f===v&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);
|
||||
return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===v)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||
|
||||
a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var ya=/[\n\t]/g,ca=/\s+/,Wa=/\r/g,Xa=/href|src|style/,Ya=/(button|input)/i,Za=/(button|input|object|select|textarea)/i,$a=/^(a|area)$/i,za=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=
|
||||
c(this);m.addClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className)for(var i=" "+e.className+" ",j=0,n=b.length;j<n;j++){if(i.indexOf(" "+b[j]+" ")<0)e.className+=" "+b[j]}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=c(this);m.removeClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string"||a===v)for(var b=(a||"").split(ca),
|
||||
d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var i=(" "+e.className+" ").replace(ya," "),j=0,n=b.length;j<n;j++)i=i.replace(" "+b[j]+" "," ");e.className=i.substring(1,i.length-1)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var i=c(this);i.toggleClass(a.call(this,e,i.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,i=0,j=c(this),n=b,o=
|
||||
a.split(ca);e=o[i++];){n=f?n:!j.hasClass(e);j[n?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(ya," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===v){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||
|
||||
{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i<d;i++){var j=e[i];if(j.selected){a=c(j).val();if(b)return a;f.push(a)}}return f}if(za.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Wa,"")}return v}var n=c.isFunction(a);return this.each(function(o){var m=c(this),s=a;if(this.nodeType===1){if(n)s=a.call(this,o,m.val());
|
||||
if(typeof s==="number")s+="";if(c.isArray(s)&&za.test(this.type))this.checked=c.inArray(m.val(),s)>=0;else if(c.nodeName(this,"select")){var x=c.makeArray(s);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),x)>=0});if(!x.length)this.selectedIndex=-1}else this.value=s}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return v;if(f&&b in c.attrFn)return c(a)[b](d);
|
||||
f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==v;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Xa.test(b);if(b in a&&f&&!i){if(e){b==="type"&&Ya.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Za.test(a.nodeName)||$a.test(a.nodeName)&&a.href?0:v;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=
|
||||
""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?v:a}return c.style(a,b,d)}});var ab=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==z&&!a.frameElement)a=z;if(!d.guid)d.guid=c.guid++;if(f!==v){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j=
|
||||
function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):v};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var n,o=0;n=b[o++];){var m=n.split(".");n=m.shift();if(o>1){d=c.proxy(d);if(f!==v)d.data=f}d.type=m.slice(0).sort().join(".");var s=e[n],x=this.special[n]||{};if(!s){s=e[n]={};if(!x.setup||x.setup.call(a,f,m,d)===false)if(a.addEventListener)a.addEventListener(n,i,false);else a.attachEvent&&a.attachEvent("on"+n,i)}if(x.add)if((m=x.add.call(a,
|
||||
d,f,m,s))&&c.isFunction(m)){m.guid=m.guid||d.guid;m.data=m.data||d.data;m.type=m.type||d.type;d=m}s[d.guid]=d;this.global[n]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===v||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);for(var n=0;i=b[n++];){var o=i.split(".");i=o.shift();var m=!o.length,s=c.map(o.slice(0).sort(),ab);s=new RegExp("(^|\\.)"+
|
||||
s.join("\\.(?:.*\\.)?")+"(\\.|$)");var x=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var A in f[i])if(m||s.test(f[i][A].type))delete f[i][A];x.remove&&x.remove.call(a,o,j);for(e in f[i])break;if(!e){if(!x.teardown||x.teardown.call(a,o)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(A=c.data(a,"handle"))A.elem=null;c.removeData(a,
|
||||
"events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return v;a.result=v;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,
|
||||
b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){d=a.target;var j;if(!(c.nodeName(d,"a")&&e==="click")&&!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){try{if(d[e]){if(j=d["on"+e])d["on"+e]=null;this.triggered=true;d[e]()}}catch(n){}if(j)d["on"+e]=j;this.triggered=false}}},handle:function(a){var b,
|
||||
d;a=arguments[0]=c.event.fix(a||z.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==v){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
|
||||
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||r;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=r.documentElement;d=r.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
|
||||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==v)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;b.liveProxy=a;c.event.add(this,b.live,na,b)},remove:function(a){if(a.length){var b=
|
||||
0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],na)}},special:{}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};
|
||||
c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y};var Aa=function(a){for(var b=
|
||||
a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ba=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ba:Aa,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ba:Aa)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!==
|
||||
"form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return ma("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return ma("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this,
|
||||
"keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var da=/textarea|input|select/i;function Ca(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ea(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Ca(d);if(a.type!=="focusout"||
|
||||
d.type!=="radio")c.data(d,"_change_data",e);if(!(f===v||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}}c.event.special.change={filters:{focusout:ea,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ea.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ea.call(this,a)},beforeactivate:function(a){a=
|
||||
a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Ca(a))}},setup:function(a,b,d){for(var f in T)c.event.add(this,f+".specialChange."+d.guid,T[f]);return da.test(this.nodeName)},remove:function(a,b){for(var d in T)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),T[d]);return da.test(this.nodeName)}};var T=c.event.special.change.filters}r.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,
|
||||
f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=v}var j=b==="one"?c.proxy(e,function(n){c(this).unbind(n,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a,
|
||||
b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+
|
||||
a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e){var i,j=0;if(c.isFunction(f)){e=f;f=v}for(d=(d||"").split(/\s+/);(i=d[j++])!=null;){i=i==="focus"?"focusin":i==="blur"?"focusout":i==="hover"?d.push("mouseleave")&&"mouseenter":i;b==="live"?c(this.context).bind(oa(i,this.selector),{data:f,selector:this.selector,
|
||||
live:i},e):c(this.context).unbind(oa(i,this.selector),e?{guid:e.guid+this.selector+i}:null)}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});z.attachEvent&&!z.addEventListener&&z.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
|
||||
(function(){function a(g){for(var h="",k,l=0;g[l];l++){k=g[l];if(k.nodeType===3||k.nodeType===4)h+=k.nodeValue;else if(k.nodeType!==8)h+=a(k.childNodes)}return h}function b(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===k){y=l[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=k;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}l[q]=y}}}function d(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===
|
||||
k){y=l[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=k;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(o.filter(h,[t]).length>0){y=t;break}}t=t[g]}l[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,i=Object.prototype.toString,j=false,n=true;[0,0].sort(function(){n=false;return 0});var o=function(g,h,k,l){k=k||[];var q=h=h||r;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||
|
||||
typeof g!=="string")return k;for(var p=[],u,t,y,R,H=true,M=w(h),I=g;(f.exec(""),u=f.exec(I))!==null;){I=u[3];p.push(u[1]);if(u[2]){R=u[3];break}}if(p.length>1&&s.exec(g))if(p.length===2&&m.relative[p[0]])t=fa(p[0]+p[1],h);else for(t=m.relative[p[0]]?[h]:o(p.shift(),h);p.length;){g=p.shift();if(m.relative[g])g+=p.shift();t=fa(g,t)}else{if(!l&&p.length>1&&h.nodeType===9&&!M&&m.match.ID.test(p[0])&&!m.match.ID.test(p[p.length-1])){u=o.find(p.shift(),h,M);h=u.expr?o.filter(u.expr,u.set)[0]:u.set[0]}if(h){u=
|
||||
l?{expr:p.pop(),set:A(l)}:o.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=u.expr?o.filter(u.expr,u.set):u.set;if(p.length>0)y=A(t);else H=false;for(;p.length;){var D=p.pop();u=D;if(m.relative[D])u=p.pop();else D="";if(u==null)u=h;m.relative[D](y,u,M)}}else y=[]}y||(y=t);y||o.error(D||g);if(i.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))k.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&
|
||||
y[g].nodeType===1&&k.push(t[g]);else k.push.apply(k,y);else A(y,k);if(R){o(R,q,k,l);o.uniqueSort(k)}return k};o.uniqueSort=function(g){if(C){j=n;g.sort(C);if(j)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};o.matches=function(g,h){return o(g,null,null,h)};o.find=function(g,h,k){var l,q;if(!g)return[];for(var p=0,u=m.order.length;p<u;p++){var t=m.order[p];if(q=m.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");l=m.find[t](q,
|
||||
h,k);if(l!=null){g=g.replace(m.match[t],"");break}}}}l||(l=h.getElementsByTagName("*"));return{set:l,expr:g}};o.filter=function(g,h,k,l){for(var q=g,p=[],u=h,t,y,R=h&&h[0]&&w(h[0]);g&&h.length;){for(var H in m.filter)if((t=m.leftMatch[H].exec(g))!=null&&t[2]){var M=m.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(u===p)p=[];if(m.preFilter[H])if(t=m.preFilter[H](t,u,k,p,l,R)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=u[U])!=null;U++)if(D){I=M(D,t,U,u);var Da=
|
||||
l^!!I;if(k&&I!=null)if(Da)y=true;else u[U]=false;else if(Da){p.push(D);y=true}}if(I!==v){k||(u=p);g=g.replace(m.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)o.error(g);else break;q=g}return u};o.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var m=o.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
|
||||
TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,h){var k=typeof h==="string",l=k&&!/\W/.test(h);k=k&&!l;if(l)h=h.toLowerCase();l=0;for(var q=g.length,
|
||||
p;l<q;l++)if(p=g[l]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[l]=k||p&&p.nodeName.toLowerCase()===h?p||false:p===h}k&&o.filter(h,g,true)},">":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var l=0,q=g.length;l<q;l++){var p=g[l];if(p){k=p.parentNode;g[l]=k.nodeName.toLowerCase()===h?k:false}}}else{l=0;for(q=g.length;l<q;l++)if(p=g[l])g[l]=k?p.parentNode:p.parentNode===h;k&&o.filter(h,g,true)}},"":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=
|
||||
h=h.toLowerCase();q=b}q("parentNode",h,l,g,p,k)},"~":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,l,g,p,k)}},find:{ID:function(g,h,k){if(typeof h.getElementById!=="undefined"&&!k)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var k=[];h=h.getElementsByName(g[1]);for(var l=0,q=h.length;l<q;l++)h[l].getAttribute("name")===g[1]&&k.push(h[l]);return k.length===0?null:k}},
|
||||
TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,k,l,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var u;(u=h[p])!=null;p++)if(u)if(q^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&
|
||||
"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);
|
||||
return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===
|
||||
g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2===
|
||||
0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return h<k[3]-0},gt:function(g,h,k){return h>k[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k<l;k++)if(h[k]===g)return false;return true}else o.error("Syntax error, unrecognized expression: "+
|
||||
q)},CHILD:function(g,h){var k=h[1],l=g;switch(k){case "only":case "first":for(;l=l.previousSibling;)if(l.nodeType===1)return false;if(k==="first")return true;l=g;case "last":for(;l=l.nextSibling;)if(l.nodeType===1)return false;return true;case "nth":k=h[2];var q=h[3];if(k===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var u=0;for(l=p.firstChild;l;l=l.nextSibling)if(l.nodeType===1)l.nodeIndex=++u;p.sizcache=h}g=g.nodeIndex-q;return k===0?g===0:g%k===0&&g/k>=
|
||||
0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="?
|
||||
k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};
|
||||
try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k<l;k++)h.push(g[k]);else for(k=0;g[k];k++)h.push(g[k]);return h}}var C;if(r.documentElement.compareDocumentPosition)C=function(g,h){if(!g.compareDocumentPosition||!h.compareDocumentPosition){if(g==h)j=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===
|
||||
h?0:1;if(g===0)j=true;return g};else if("sourceIndex"in r.documentElement)C=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)j=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)j=true;return g};else if(r.createRange)C=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)j=true;return g.ownerDocument?-1:1}var k=g.ownerDocument.createRange(),l=h.ownerDocument.createRange();k.setStart(g,0);k.setEnd(g,0);l.setStart(h,0);l.setEnd(h,0);g=k.compareBoundaryPoints(Range.START_TO_END,
|
||||
l);if(g===0)j=true;return g};(function(){var g=r.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id");
|
||||
return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href",
|
||||
2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===
|
||||
0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[],
|
||||
l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)o(g,h[q],k);return o.filter(l,k)};c.find=o;c.expr=o.selectors;c.expr[":"]=c.expr.filters;c.unique=o.uniqueSort;c.getText=a;c.isXMLDoc=w;c.contains=E})();var bb=/Until$/,cb=/^(?:parents|prevUntil|prevAll)/,db=/,/;Q=Array.prototype.slice;var Ea=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,i){return!!b.call(e,i,e)===d});else if(b.nodeType)return c.grep(a,
|
||||
function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Qa.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;c.find(a,this[f],b);if(f>0)for(var i=d;i<b.length;i++)for(var j=0;j<d;j++)if(b[j]===b[i]){b.splice(i--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=
|
||||
0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ea(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ea(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e<n;e++){j=a[e];i[j]||(i[j]=c.expr.match.POS.test(j)?c(j,b||this.context):j)}for(;f&&f.ownerDocument&&f!==b;){for(j in i){e=i[j];if(e.jquery?e.index(f)>
|
||||
-1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),
|
||||
a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},
|
||||
nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):
|
||||
e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==
|
||||
b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/<tbody/i,gb=/<|&\w+;/,sa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ia=function(a,b,d){return eb.test(d)?a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],
|
||||
col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)},
|
||||
wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?
|
||||
d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,
|
||||
false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&
|
||||
!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/<script/i.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(Ha.exec(a)||
|
||||
["",""])[1].toLowerCase()]){a=a.replace(Ga,Ia);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var i=c(this),j=i.html();i.empty().append(function(){return a.call(this,e,j)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,
|
||||
b,f))});else a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(s){return c.nodeName(s,"table")?s.getElementsByTagName("tbody")[0]||s.appendChild(s.ownerDocument.createElement("tbody")):s}var e,i,j=a[0],n=[];if(!c.support.checkClone&&arguments.length===3&&typeof j===
|
||||
"string"&&sa.test(j))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(j))return this.each(function(s){var x=c(this);a[0]=j.call(this,s,b?x.html():v);x.domManip(a,b,d)});if(this[0]){e=a[0]&&a[0].parentNode&&a[0].parentNode.nodeType===11?{fragment:a[0].parentNode}:ra(a,this,n);if(i=e.fragment.firstChild){b=b&&c.nodeName(i,"tr");for(var o=0,m=this.length;o<m;o++)d.call(b?f(this[o],i):this[o],e.cacheable||this.length>1||o>0?e.fragment.cloneNode(true):e.fragment)}n&&c.each(n,
|
||||
Ma)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);for(var e=0,i=d.length;e<i;e++){var j=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),j);f=f.concat(j)}return this.pushStack(f,a,d.selector)}});c.each({remove:function(a,b){if(!a||c.filter(a,[this]).length){if(!b&&this.nodeType===1){c.cleanData(this.getElementsByTagName("*"));c.cleanData([this])}this.parentNode&&
|
||||
this.parentNode.removeChild(this)}},empty:function(){for(this.nodeType===1&&c.cleanData(this.getElementsByTagName("*"));this.firstChild;)this.removeChild(this.firstChild)}},function(a,b){c.fn[a]=function(){return this.each(b,arguments)}});c.extend({clean:function(a,b,d,f){b=b||r;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||r;var e=[];c.each(a,function(i,j){if(typeof j==="number")j+="";if(j){if(typeof j==="string"&&!gb.test(j))j=b.createTextNode(j);else if(typeof j===
|
||||
"string"){j=j.replace(Ga,Ia);var n=(Ha.exec(j)||["",""])[1].toLowerCase(),o=F[n]||F._default,m=o[0];i=b.createElement("div");for(i.innerHTML=o[1]+j+o[2];m--;)i=i.lastChild;if(!c.support.tbody){m=fb.test(j);n=n==="table"&&!m?i.firstChild&&i.firstChild.childNodes:o[1]==="<table>"&&!m?i.childNodes:[];for(o=n.length-1;o>=0;--o)c.nodeName(n[o],"tbody")&&!n[o].childNodes.length&&n[o].parentNode.removeChild(n[o])}!c.support.leadingWhitespace&&V.test(j)&&i.insertBefore(b.createTextNode(V.exec(j)[0]),i.firstChild);
|
||||
j=c.makeArray(i.childNodes)}if(j.nodeType)e.push(j);else e=c.merge(e,j)}});if(d)for(a=0;e[a];a++)if(f&&c.nodeName(e[a],"script")&&(!e[a].type||e[a].type.toLowerCase()==="text/javascript"))f.push(e[a].parentNode?e[a].parentNode.removeChild(e[a]):e[a]);else{e[a].nodeType===1&&e.splice.apply(e,[a+1,0].concat(c.makeArray(e[a].getElementsByTagName("script"))));d.appendChild(e[a])}return e},cleanData:function(a){for(var b=0,d;(d=a[b])!=null;b++){c.event.remove(d);c.removeData(d)}}});var hb=/z-?index|font-?weight|opacity|zoom|line-?height/i,
|
||||
Ja=/alpha\([^)]*\)/,Ka=/opacity=([^)]*)/,ga=/float/i,ha=/-([a-z])/ig,ib=/([A-Z])/g,jb=/^-?\d+(?:px)?$/i,kb=/^-?\d/,lb={position:"absolute",visibility:"hidden",display:"block"},mb=["Left","Right"],nb=["Top","Bottom"],ob=r.defaultView&&r.defaultView.getComputedStyle,La=c.support.cssFloat?"cssFloat":"styleFloat",ia=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===v)return c.curCSS(d,f);if(typeof e==="number"&&!hb.test(f))e+="px";c.style(d,f,e)})};
|
||||
c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return v;if((b==="width"||b==="height")&&parseFloat(d)<0)d=v;var f=a.style||a,e=d!==v;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=Ja.test(a)?a.replace(Ja,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Ka.exec(f.filter)[1])/100+"":""}if(ga.test(b))b=La;b=b.replace(ha,ia);if(e)f[b]=d;return f[b]},css:function(a,
|
||||
b,d,f){if(b==="width"||b==="height"){var e,i=b==="width"?mb:nb;function j(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(i,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,"border"+this+"Width",true))||0})}a.offsetWidth!==0?j():c.swap(a,lb,j);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&
|
||||
a.currentStyle){f=Ka.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ga.test(b))b=La;if(!d&&e&&e[b])f=e[b];else if(ob){if(ga.test(b))b="float";b=b.replace(ib,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ha,ia);f=a.currentStyle[b]||a.currentStyle[d];if(!jb.test(f)&&kb.test(f)){b=e.left;var i=a.runtimeStyle.left;a.runtimeStyle.left=
|
||||
a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=i}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var pb=
|
||||
J(),qb=/<script(.|\s)*?\/script>/gi,rb=/select|textarea/i,sb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ja=/\?/,tb=/(\?|&)_=.*?(&|$)/,ub=/^(\w+:)?\/\/([^\/?#]+)/,vb=/%20/g;c.fn.extend({_load:c.fn.load,load:function(a,b,d){if(typeof a!=="string")return this._load(a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=
|
||||
c.param(b,c.ajaxSettings.traditional);f="POST"}var i=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(j,n){if(n==="success"||n==="notmodified")i.html(e?c("<div />").append(j.responseText.replace(qb,"")).find(e):j.responseText);d&&i.each(d,[j.responseText,n,j])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&
|
||||
(this.checked||rb.test(this.nodeName)||sb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,
|
||||
b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:z.XMLHttpRequest&&(z.location.protocol!=="file:"||!z.ActiveXObject)?function(){return new z.XMLHttpRequest}:
|
||||
function(){try{return new z.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&e.success.call(o,n,j,w);e.global&&f("ajaxSuccess",[w,e])}function d(){e.complete&&e.complete.call(o,w,j);e.global&&f("ajaxComplete",[w,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}
|
||||
function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),i,j,n,o=a&&a.context||e,m=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(m==="GET")N.test(e.url)||(e.url+=(ja.test(e.url)?"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||
|
||||
N.test(e.url))){i=e.jsonpCallback||"jsonp"+pb++;if(e.data)e.data=(e.data+"").replace(N,"="+i+"$1");e.url=e.url.replace(N,"="+i+"$1");e.dataType="script";z[i]=z[i]||function(q){n=q;b();d();z[i]=v;try{delete z[i]}catch(p){}A&&A.removeChild(B)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===false&&m==="GET"){var s=J(),x=e.url.replace(tb,"$1_="+s+"$2");e.url=x+(x===e.url?(ja.test(e.url)?"&":"?")+"_="+s:"")}if(e.data&&m==="GET")e.url+=(ja.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&
|
||||
c.event.trigger("ajaxStart");s=(s=ub.exec(e.url))&&(s[1]&&s[1]!==location.protocol||s[2]!==location.host);if(e.dataType==="script"&&m==="GET"&&s){var A=r.getElementsByTagName("head")[0]||r.documentElement,B=r.createElement("script");B.src=e.url;if(e.scriptCharset)B.charset=e.scriptCharset;if(!i){var C=false;B.onload=B.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;b();d();B.onload=B.onreadystatechange=null;A&&B.parentNode&&
|
||||
A.removeChild(B)}}}A.insertBefore(B,A.firstChild);return v}var E=false,w=e.xhr();if(w){e.username?w.open(m,e.url,e.async,e.username,e.password):w.open(m,e.url,e.async);try{if(e.data||a&&a.contentType)w.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[e.url]);c.etag[e.url]&&w.setRequestHeader("If-None-Match",c.etag[e.url])}s||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",
|
||||
e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(fa){}if(e.beforeSend&&e.beforeSend.call(o,w,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");w.abort();return false}e.global&&f("ajaxSend",[w,e]);var g=w.onreadystatechange=function(q){if(!w||w.readyState===0||q==="abort"){E||d();E=true;if(w)w.onreadystatechange=c.noop}else if(!E&&w&&(w.readyState===4||q==="timeout")){E=true;w.onreadystatechange=c.noop;j=q==="timeout"?"timeout":!c.httpSuccess(w)?
|
||||
"error":e.ifModified&&c.httpNotModified(w,e.url)?"notmodified":"success";var p;if(j==="success")try{n=c.httpData(w,e.dataType,e)}catch(u){j="parsererror";p=u}if(j==="success"||j==="notmodified")i||b();else c.handleError(e,w,j,p);d();q==="timeout"&&w.abort();if(e.async)w=null}};try{var h=w.abort;w.abort=function(){w&&h.call(w);g("abort")}}catch(k){}e.async&&e.timeout>0&&setTimeout(function(){w&&!E&&g("timeout")},e.timeout);try{w.send(m==="POST"||m==="PUT"||m==="DELETE"?e.data:null)}catch(l){c.handleError(e,
|
||||
w,null,l);d()}e.async||g();return w}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=
|
||||
f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(j,n){if(c.isArray(n))c.each(n,
|
||||
function(o,m){b?f(j,m):d(j+"["+(typeof m==="object"||c.isArray(m)?o:"")+"]",m)});else!b&&n!=null&&typeof n==="object"?c.each(n,function(o,m){d(j+"["+o+"]",m)}):f(j,n)}function f(j,n){n=c.isFunction(n)?n():n;e[e.length]=encodeURIComponent(j)+"="+encodeURIComponent(n)}var e=[];if(b===v)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var i in a)d(i,a[i]);return e.join("&").replace(vb,"+")}});var ka={},wb=/toggle|show|hide/,xb=/^([+-]=)?([\d+-.]+)(.*)$/,
|
||||
W,ta=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(ka[d])f=ka[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();
|
||||
ka[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&
|
||||
c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var i=c.extend({},e),j,n=this.nodeType===1&&c(this).is(":hidden"),
|
||||
o=this;for(j in a){var m=j.replace(ha,ia);if(j!==m){a[m]=a[j];delete a[j];j=m}if(a[j]==="hide"&&n||a[j]==="show"&&!n)return i.complete.call(this);if((j==="height"||j==="width")&&this.style){i.display=c.css(this,"display");i.overflow=this.style.overflow}if(c.isArray(a[j])){(i.specialEasing=i.specialEasing||{})[j]=a[j][1];a[j]=a[j][0]}}if(i.overflow!=null)this.style.overflow="hidden";i.curAnim=c.extend({},a);c.each(a,function(s,x){var A=new c.fx(o,i,s);if(wb.test(x))A[x==="toggle"?n?"show":"hide":x](a);
|
||||
else{var B=xb.exec(x),C=A.cur(true)||0;if(B){x=parseFloat(B[2]);var E=B[3]||"px";if(E!=="px"){o.style[s]=(x||1)+E;C=(x||1)/A.cur(true)*C;o.style[s]=C+E}if(B[1])x=(B[1]==="-="?-1:1)*x+C;A.custom(C,x,E)}else A.custom(C,x,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",
|
||||
1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration==="number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,
|
||||
b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==
|
||||
null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(i){return e.step(i)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop===
|
||||
"width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=
|
||||
this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=
|
||||
c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=
|
||||
null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in r.documentElement?function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),
|
||||
f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(s){c.offset.setOffset(this,a,s)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=
|
||||
b,e=b.ownerDocument,i,j=e.documentElement,n=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var o=b.offsetTop,m=b.offsetLeft;(b=b.parentNode)&&b!==n&&b!==j;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;i=e?e.getComputedStyle(b,null):b.currentStyle;o-=b.scrollTop;m-=b.scrollLeft;if(b===d){o+=b.offsetTop;m+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){o+=parseFloat(i.borderTopWidth)||
|
||||
0;m+=parseFloat(i.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&i.overflow!=="visible"){o+=parseFloat(i.borderTopWidth)||0;m+=parseFloat(i.borderLeftWidth)||0}f=i}if(f.position==="relative"||f.position==="static"){o+=n.offsetTop;m+=n.offsetLeft}if(c.offset.supportsFixedPosition&&f.position==="fixed"){o+=Math.max(j.scrollTop,n.scrollTop);m+=Math.max(j.scrollLeft,n.scrollLeft)}return{top:o,left:m}};c.offset={initialize:function(){var a=r.body,b=r.createElement("div"),
|
||||
d,f,e,i=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);
|
||||
d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i;a.removeChild(b);c.offset.initialize=c.noop},
|
||||
bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),i=parseInt(c.curCSS(a,"top",true),10)||0,j=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,d,e);d={top:b.top-e.top+i,left:b.left-
|
||||
e.left+j};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=
|
||||
this.offsetParent||r.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],i;if(!e)return null;if(f!==v)return this.each(function(){if(i=ua(this))i.scrollTo(!a?f:c(i).scrollLeft(),a?f:c(i).scrollTop());else this[d]=f});else return(i=ua(e))?"pageXOffset"in i?i[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&i.document.documentElement[d]||i.document.body[d]:e[d]}});
|
||||
c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(i){var j=c(this);j[d](f.call(this,i,j[d]()))});return"scrollTo"in e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||
|
||||
e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===v?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});z.jQuery=z.$=c})(window);
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
http://www.JSON.org/json2.js
|
||||
2010-03-20
|
||||
Public Domain.
|
||||
*/
|
||||
if (!this.JSON) {
|
||||
this.JSON = {};
|
||||
}
|
||||
|
||||
(function () {
|
||||
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
|
||||
Date.prototype.toJSON = function (key) {
|
||||
|
||||
return isFinite(this.valueOf()) ?
|
||||
this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z' : null;
|
||||
};
|
||||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
Boolean.prototype.toJSON = function (key) {
|
||||
return this.valueOf();
|
||||
};
|
||||
}
|
||||
|
||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
rep;
|
||||
|
||||
|
||||
function quote(string) {
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ?
|
||||
'"' + string.replace(escapable, function (a) {
|
||||
var c = meta[a];
|
||||
return typeof c === 'string' ? c :
|
||||
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' :
|
||||
'"' + string + '"';
|
||||
}
|
||||
|
||||
|
||||
function str(key, holder) {
|
||||
var i,
|
||||
k,
|
||||
v,
|
||||
length,
|
||||
mind = gap,
|
||||
partial,
|
||||
value = holder[key];
|
||||
|
||||
if (value && typeof value === 'object' &&
|
||||
typeof value.toJSON === 'function') {
|
||||
value = value.toJSON(key);
|
||||
}
|
||||
|
||||
if (typeof rep === 'function') {
|
||||
value = rep.call(holder, key, value);
|
||||
}
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return quote(value);
|
||||
|
||||
case 'number':
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
return String(value);
|
||||
|
||||
case 'object':
|
||||
if (!value) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
gap += indent;
|
||||
partial = [];
|
||||
|
||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
||||
length = value.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
partial[i] = str(i, value) || 'null';
|
||||
}
|
||||
|
||||
v = partial.length === 0 ? '[]' :
|
||||
gap ? '[\n' + gap +
|
||||
partial.join(',\n' + gap) + '\n' +
|
||||
mind + ']' :
|
||||
'[' + partial.join(',') + ']';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
|
||||
if (rep && typeof rep === 'object') {
|
||||
length = rep.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
k = rep[i];
|
||||
if (typeof k === 'string') {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (k in value) {
|
||||
if (Object.hasOwnProperty.call(value, k)) {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the member texts together, separated with commas,
|
||||
// and wrap them in braces.
|
||||
|
||||
v = partial.length === 0 ? '{}' :
|
||||
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
|
||||
mind + '}' : '{' + partial.join(',') + '}';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
|
||||
// The stringify method takes a value and an optional replacer, and an optional
|
||||
// space parameter, and returns a JSON text. The replacer can be a function
|
||||
// that can replace values, or an array of strings that will select the keys.
|
||||
// A default replacer method can be provided. Use of the space parameter can
|
||||
// produce text that is more easily readable.
|
||||
|
||||
var i;
|
||||
gap = '';
|
||||
indent = '';
|
||||
|
||||
if (typeof space === 'number') {
|
||||
for (i = 0; i < space; i += 1) {
|
||||
indent += ' ';
|
||||
}
|
||||
|
||||
} else if (typeof space === 'string') {
|
||||
indent = space;
|
||||
}
|
||||
|
||||
rep = replacer;
|
||||
if (replacer && typeof replacer !== 'function' &&
|
||||
(typeof replacer !== 'object' ||
|
||||
typeof replacer.length !== 'number')) {
|
||||
throw new Error('JSON.stringify');
|
||||
}
|
||||
|
||||
return str('', {'': value});
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
JSON.parse = function (text, reviver) {
|
||||
var j;
|
||||
|
||||
function walk(holder, key) {
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
for (k in value) {
|
||||
if (Object.hasOwnProperty.call(value, k)) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}
|
||||
|
||||
text = String(text);
|
||||
cx.lastIndex = 0;
|
||||
if (cx.test(text)) {
|
||||
text = text.replace(cx, function (a) {
|
||||
return '\\u' +
|
||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
if (/^[\],:{}\s]*$/.
|
||||
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
|
||||
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
|
||||
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||
j = eval('(' + text + ')');
|
||||
return typeof reviver === 'function' ?
|
||||
walk({'': j}, '') : j;
|
||||
}
|
||||
|
||||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
}());
|
||||
@@ -0,0 +1,21 @@
|
||||
AjaxIM.l10n = {
|
||||
dayNames: [
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
|
||||
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
|
||||
],
|
||||
monthNames: [
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||
"January", "February", "March", "April", "May", "June", "July", "August", "September",
|
||||
"October", "November", "December"
|
||||
],
|
||||
|
||||
chatOffline: '%s signed off.',
|
||||
chatOnline: '%s became available.',
|
||||
chatAway: '%s went away.',
|
||||
|
||||
notConnected: 'You are currently not connected or the server is not available. ' +
|
||||
'Please ensure that you are signed in and try again.',
|
||||
notConnectedTip: 'You are currently not connected.',
|
||||
|
||||
defaultAway: 'I\'m away.'
|
||||
};
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
|
||||
* Digest Algorithm, as defined in RFC 1321.
|
||||
* Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
|
||||
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||
* Distributed under the BSD License
|
||||
* See http://pajhome.org.uk/crypt/md5 for more info.
|
||||
*/
|
||||
|
||||
var md5 = (function() {
|
||||
/*
|
||||
* Calculate the MD5 of an array of little-endian words, and a bit length
|
||||
*/
|
||||
function core_md5(x, len) {
|
||||
/* append padding */
|
||||
x[len >> 5] |= 0x80 << ((len) % 32);
|
||||
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
||||
|
||||
var a = 1732584193;
|
||||
var b = -271733879;
|
||||
var c = -1732584194;
|
||||
var d = 271733878;
|
||||
|
||||
for(var i = 0; i < x.length; i += 16) {
|
||||
var olda = a;
|
||||
var oldb = b;
|
||||
var oldc = c;
|
||||
var oldd = d;
|
||||
|
||||
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
|
||||
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
|
||||
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
|
||||
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
|
||||
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
|
||||
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
|
||||
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
|
||||
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
|
||||
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
|
||||
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
|
||||
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
|
||||
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
|
||||
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
|
||||
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
|
||||
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
|
||||
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
|
||||
|
||||
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
|
||||
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
|
||||
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
|
||||
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
|
||||
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
|
||||
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
|
||||
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
|
||||
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
|
||||
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
|
||||
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
|
||||
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
|
||||
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
|
||||
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
|
||||
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
|
||||
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
|
||||
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
|
||||
|
||||
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
|
||||
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
|
||||
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
|
||||
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
|
||||
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
|
||||
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
|
||||
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
|
||||
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
|
||||
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
|
||||
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
|
||||
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
|
||||
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
|
||||
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
|
||||
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
|
||||
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
|
||||
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
|
||||
|
||||
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
|
||||
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
|
||||
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
|
||||
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
|
||||
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
|
||||
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
|
||||
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
|
||||
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
|
||||
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
|
||||
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
|
||||
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
|
||||
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
|
||||
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
|
||||
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
|
||||
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
|
||||
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
|
||||
|
||||
a = safe_add(a, olda);
|
||||
b = safe_add(b, oldb);
|
||||
c = safe_add(c, oldc);
|
||||
d = safe_add(d, oldd);
|
||||
}
|
||||
return [a, b, c, d];
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions implement the four basic operations the algorithm uses.
|
||||
*/
|
||||
function md5_cmn(q, a, b, x, s, t) {
|
||||
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
|
||||
}
|
||||
function md5_ff(a, b, c, d, x, s, t) {
|
||||
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
|
||||
}
|
||||
function md5_gg(a, b, c, d, x, s, t) {
|
||||
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
|
||||
}
|
||||
function md5_hh(a, b, c, d, x, s, t) {
|
||||
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
function md5_ii(a, b, c, d, x, s, t) {
|
||||
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the HMAC-MD5, of a key and some data
|
||||
*/
|
||||
function core_hmac_md5(key, data) {
|
||||
var bkey = str2binl(key);
|
||||
if(bkey.length > 16) bkey = core_md5(bkey, key.length * md5.chrsz);
|
||||
|
||||
var ipad = [], opad = [];
|
||||
for(var i = 0; i < 16; i++) {
|
||||
ipad[i] = bkey[i] ^ 0x36363636;
|
||||
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
||||
}
|
||||
|
||||
var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * md5.chrsz);
|
||||
return core_md5(opad.concat(hash), 512 + 128);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
||||
* to work around bugs in some JS interpreters.
|
||||
*/
|
||||
function safe_add(x, y) {
|
||||
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xFFFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitwise rotate a 32-bit number to the left.
|
||||
*/
|
||||
function bit_rol(num, cnt) {
|
||||
return (num << cnt) | (num >>> (32 - cnt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a string to an array of little-endian words
|
||||
* If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
|
||||
*/
|
||||
function str2binl(str) {
|
||||
var bin = [], chrsz = md5.chrsz;
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for(var i = 0; i < str.length * chrsz; i += chrsz)
|
||||
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
|
||||
return bin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a string
|
||||
*/
|
||||
function binl2str(bin) {
|
||||
var str = "", chrsz = md5.chrsz;
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for(var i = 0; i < bin.length * 32; i += chrsz)
|
||||
str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a hex string.
|
||||
*/
|
||||
function binl2hex(binarray) {
|
||||
var hex_tab = md5.hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
var str = "";
|
||||
for(var i = 0; i < binarray.length * 4; i++) {
|
||||
str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
|
||||
hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a base-64 string
|
||||
*/
|
||||
function binl2b64(binarray) {
|
||||
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var str = "";
|
||||
for(var i = 0; i < binarray.length * 4; i += 3) {
|
||||
var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
|
||||
| (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
|
||||
| ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
|
||||
for(var j = 0; j < 4; j++) {
|
||||
if(i * 8 + j * 6 > binarray.length * 32) str += md5.b64pad;
|
||||
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
return {
|
||||
/*
|
||||
* Configurable variables. You may need to tweak these to be compatible with
|
||||
* the server-side, but the defaults work in most cases.
|
||||
*/
|
||||
hexcase: 0, // hex output format. 0 - lowercase; 1 - uppercase
|
||||
b64pad: "", // base-64 pad character. "=" for strict RFC compliance
|
||||
chrsz: 8, // bits per input character. 8 - ASCII; 16 - Unicode
|
||||
|
||||
/*
|
||||
* These are the functions you'll usually want to call
|
||||
* They take string arguments and return either hex or base-64 encoded strings
|
||||
*/
|
||||
hex:function( s ) {
|
||||
return binl2hex( core_md5( str2binl( s ), s.length * md5.chrsz ) );
|
||||
},
|
||||
|
||||
base64:function( s ) {
|
||||
return binl2b64( core_md5( str2binl( s ), s.length * md5.chrsz ) );
|
||||
},
|
||||
|
||||
string:function( s ) {
|
||||
return binl2str( core_md5( str2binl( s ), s.length * md5.chrsz ) );
|
||||
},
|
||||
|
||||
hmac:{
|
||||
hex:function( key, data ) {
|
||||
return binl2hex( core_hmac_md5( key, data ) );
|
||||
},
|
||||
|
||||
base64:function( key, data ) {
|
||||
return binl2b64( core_hmac_md5( key, data ) );
|
||||
},
|
||||
|
||||
string:function( key, data ) {
|
||||
return binl2str( core_hmac_md5( key, data ) );
|
||||
}
|
||||
},
|
||||
|
||||
test:function() { // Perform a simple self-test to see if the VM is working
|
||||
return this.hex("abc") == "900150983cd24fb0d6963f7d28e17f72";
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,2 @@
|
||||
$(function() { AjaxIM.init(); });
|
||||
})(jQuery || $);
|
||||
@@ -0,0 +1,123 @@
|
||||
/* Copyright (c) 2010-2011 Marcus Westin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
var store = (function(){
|
||||
var api = {},
|
||||
win = window,
|
||||
doc = win.document,
|
||||
localStorageName = 'localStorage',
|
||||
globalStorageName = 'globalStorage',
|
||||
namespace = '__storejs__',
|
||||
storage
|
||||
|
||||
api.disabled = false
|
||||
api.set = function(key, value) {}
|
||||
api.get = function(key) {}
|
||||
api.remove = function(key) {}
|
||||
api.clear = function() {}
|
||||
api.transact = function(key, transactionFn) {
|
||||
var val = api.get(key)
|
||||
if (typeof val == 'undefined') { val = {} }
|
||||
transactionFn(val)
|
||||
api.set(key, val)
|
||||
}
|
||||
|
||||
api.serialize = function(value) {
|
||||
return JSON.stringify(value)
|
||||
}
|
||||
api.deserialize = function(value) {
|
||||
if (typeof value != 'string') { return undefined }
|
||||
return JSON.parse(value)
|
||||
}
|
||||
|
||||
// Functions to encapsulate questionable FireFox 3.6.13 behavior
|
||||
// when about.config::dom.storage.enabled === false
|
||||
// See https://github.com/marcuswestin/store.js/issues#issue/13
|
||||
function isLocalStorageNameSupported() {
|
||||
try { return (localStorageName in win && win[localStorageName]) }
|
||||
catch(err) { return false }
|
||||
}
|
||||
|
||||
function isGlobalStorageNameSupported() {
|
||||
try { return (globalStorageName in win && win[globalStorageName] && win[globalStorageName][win.location.hostname]) }
|
||||
catch(err) { return false }
|
||||
}
|
||||
|
||||
if (isLocalStorageNameSupported()) {
|
||||
storage = win[localStorageName]
|
||||
api.set = function(key, val) { storage.setItem(key, api.serialize(val)) }
|
||||
api.get = function(key) { return api.deserialize(storage.getItem(key)) }
|
||||
api.remove = function(key) { storage.removeItem(key) }
|
||||
api.clear = function() { storage.clear() }
|
||||
|
||||
} else if (isGlobalStorageNameSupported()) {
|
||||
storage = win[globalStorageName][win.location.hostname]
|
||||
api.set = function(key, val) { storage[key] = api.serialize(val) }
|
||||
api.get = function(key) { return api.deserialize(storage[key] && storage[key].value) }
|
||||
api.remove = function(key) { delete storage[key] }
|
||||
api.clear = function() { for (var key in storage ) { delete storage[key] } }
|
||||
|
||||
} else if (doc.documentElement.addBehavior) {
|
||||
var storage = doc.createElement('div')
|
||||
function withIEStorage(storeFunction) {
|
||||
return function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0)
|
||||
args.unshift(storage)
|
||||
// See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
|
||||
// and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
|
||||
doc.body.appendChild(storage)
|
||||
storage.addBehavior('#default#userData')
|
||||
storage.load(localStorageName)
|
||||
var result = storeFunction.apply(api, args)
|
||||
doc.body.removeChild(storage)
|
||||
return result
|
||||
}
|
||||
}
|
||||
api.set = withIEStorage(function(storage, key, val) {
|
||||
storage.setAttribute(key, api.serialize(val))
|
||||
storage.save(localStorageName)
|
||||
})
|
||||
api.get = withIEStorage(function(storage, key) {
|
||||
return api.deserialize(storage.getAttribute(key))
|
||||
})
|
||||
api.remove = withIEStorage(function(storage, key) {
|
||||
storage.removeAttribute(key)
|
||||
storage.save(localStorageName)
|
||||
})
|
||||
api.clear = withIEStorage(function(storage) {
|
||||
var attributes = storage.XMLDocument.documentElement.attributes
|
||||
storage.load(localStorageName)
|
||||
for (var i=0, attr; attr = attributes[i]; i++) {
|
||||
storage.removeAttribute(attr.name)
|
||||
}
|
||||
storage.save(localStorageName)
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
api.set(namespace, namespace)
|
||||
if (api.get(namespace) != namespace) { api.disabled = true }
|
||||
api.remove(namespace)
|
||||
} catch(e) {
|
||||
api.disabled = true
|
||||
}
|
||||
|
||||
return api
|
||||
})();
|
||||
@@ -0,0 +1,92 @@
|
||||
var Template = {
|
||||
bar:
|
||||
'<ul id="imjs-bar">' +
|
||||
'<li id="imjs-friends" class="imjs-not-connected">' +
|
||||
'<span class="imjs-tab-text"><strong>•</strong> Friends <span>(<span>0</span>)</span></span>' +
|
||||
'<div id="imjs-friends-panel" class="imjs-chatbox">' +
|
||||
'<div class="imjs-header">' +
|
||||
'<span>{username}</span>' +
|
||||
'<a href="#" class="imjs-minimize">_</a>' +
|
||||
'</div>' +
|
||||
'<div id="imjs-status-panel">' +
|
||||
'<textarea id="imjs-away-message-text"></textarea>' +
|
||||
'<div id="imjs-away-message-text-arrow"></div>' +
|
||||
'<a href="#" id="imjs-button-online" class="imjs-button"><span>•</span> Available</a>' +
|
||||
'<a href="#" id="imjs-button-away" class="imjs-button"><span>•</span> Away</a>' +
|
||||
'<a href="#" id="imjs-button-offline" class="imjs-button"><span>•</span> Offline</a>' +
|
||||
'</div>' +
|
||||
'<ul id="imjs-friends-list">' +
|
||||
'</ul>' +
|
||||
'<form>' +
|
||||
'<p><input type="text" id="imjs-search-friends" value="Search" /></p>' +
|
||||
'</form>' +
|
||||
'</div>' +
|
||||
'</li>' +
|
||||
'<li id="imjs-reconnect"><span class="imjs-tab-text">↻</span></li>' +
|
||||
'<li id="imjs-scroll-left" class="imjs-scroll">{count}</li>' +
|
||||
'<li id="imjs-scroll-right" class="imjs-scroll">{count}</li>' +
|
||||
'</ul>',
|
||||
tab:
|
||||
'<li class="imjs-tab">' +
|
||||
'<span class="imjs-tab-text"><strong>•</strong> {label} <a href="#" class="imjs-close">x</a></span>' +
|
||||
'<span class="imjs-notification">{count}</span>' +
|
||||
'<form class="imjs-chatbox">' +
|
||||
'<div>' +
|
||||
'<div class="imjs-header">' +
|
||||
'<span>{username}</span>' +
|
||||
'<a href="#" class="imjs-close">x</a>' +
|
||||
'<a href="#" class="imjs-minimize">_</a>' +
|
||||
'</div>' +
|
||||
'<ul class="imjs-msglog"></ul>' +
|
||||
'<textarea class="imjs-input"></textarea>' +
|
||||
'</div>' +
|
||||
'</form>' +
|
||||
'</li>',
|
||||
datestamp:
|
||||
'<li class="imjs-date">' +
|
||||
'<ul>' +
|
||||
'<li>' +
|
||||
'<span class="imjs-msg-time">hh:MM TT •</span>' +
|
||||
'<p class="imjs-date-date">ddd, mmmm d, yyyy</p>' +
|
||||
'</li>' +
|
||||
'</ul>' +
|
||||
'</li>',
|
||||
message_error:
|
||||
'<li class="imjs-error">' +
|
||||
'<ul>' +
|
||||
'<li>' +
|
||||
'<span class="imjs-msg-time">hh:MM TT •</span>' +
|
||||
'<p class="imjs-error-error">ddd, mmmm d, yyyy</p>' +
|
||||
'</li>' +
|
||||
'</ul>' +
|
||||
'</li>',
|
||||
message_you_outer:
|
||||
'<li class="imjs-msg-you">' +
|
||||
'<span>{username}</span>' +
|
||||
'<ul class="imjs-msg-you-container"></ul>' +
|
||||
'</li>',
|
||||
message_you:
|
||||
'<li class="imjs-msg-you-msg">' +
|
||||
'<span class="imjs-msg-time">hh:MM TT •</span>' +
|
||||
'<p>{message}</p>' +
|
||||
'</li>',
|
||||
message_them_outer:
|
||||
'<li class="imjs-msg-them">' +
|
||||
'<span>{username}</span>' +
|
||||
'<ul class="imjs-msg-them-container"></ul>' +
|
||||
'</li>',
|
||||
message_them:
|
||||
'<li class="imjs-msg-them-msg">' +
|
||||
'<span class="imjs-msg-time">hh:MM TT •</span>' +
|
||||
'<p>{message}</p>' +
|
||||
'</li>',
|
||||
group:
|
||||
'<li class="imjs-friend-group">' +
|
||||
'<span class="imjs-friend-group-header">{group}</span>' +
|
||||
'<ul></ul>' +
|
||||
'</li>',
|
||||
friend:
|
||||
'<li class="imjs-friend"><strong>•</strong><span>{username}</span><span class="imjs-friend-status">{status}</span></li>',
|
||||
tooltip:
|
||||
'<span class="imjs-tooltip"><p>{tip}</p></span>'
|
||||
};
|
||||
@@ -0,0 +1,267 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* Block TEA (xxtea) Tiny Encryption Algorithm implementation in JavaScript */
|
||||
/* (c) Chris Veness 2002-2010: www.movable-type.co.uk/tea-block.html */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* Algorithm: David Wheeler & Roger Needham, Cambridge University Computer Lab */
|
||||
/* http://www.cl.cam.ac.uk/ftp/papers/djw-rmn/djw-rmn-tea.html (1994) */
|
||||
/* http://www.cl.cam.ac.uk/ftp/users/djw3/xtea.ps (1997) */
|
||||
/* http://www.cl.cam.ac.uk/ftp/users/djw3/xxtea.ps (1998) */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
var Tea = {}; // Tea namespace
|
||||
|
||||
/*
|
||||
* encrypt text using Corrected Block TEA (xxtea) algorithm
|
||||
*
|
||||
* @param {string} plaintext String to be encrypted (multi-byte safe)
|
||||
* @param {string} password Password to be used for encryption (1st 16 chars)
|
||||
* @returns {string} encrypted text
|
||||
*/
|
||||
Tea.encrypt = function(plaintext, password) {
|
||||
if (plaintext.length == 0) return(''); // nothing to encrypt
|
||||
|
||||
// convert string to array of longs after converting any multi-byte chars to UTF-8
|
||||
var v = Tea.strToLongs(Utf8.encode(plaintext));
|
||||
if (v.length <= 1) v[1] = 0; // algorithm doesn't work for n<2 so fudge by adding a null
|
||||
// simply convert first 16 chars of password as key
|
||||
var k = Tea.strToLongs(Utf8.encode(password).slice(0,16));
|
||||
var n = v.length;
|
||||
|
||||
// ---- <TEA coding> ----
|
||||
|
||||
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
|
||||
var mx, e, q = Math.floor(6 + 52/n), sum = 0;
|
||||
|
||||
while (q-- > 0) { // 6 + 52/n operations gives between 6 & 32 mixes on each word
|
||||
sum += delta;
|
||||
e = sum>>>2 & 3;
|
||||
for (var p = 0; p < n; p++) {
|
||||
y = v[(p+1)%n];
|
||||
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
|
||||
z = v[p] += mx;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- </TEA> ----
|
||||
|
||||
var ciphertext = Tea.longsToStr(v);
|
||||
|
||||
return Base64.encode(ciphertext);
|
||||
}
|
||||
|
||||
/*
|
||||
* decrypt text using Corrected Block TEA (xxtea) algorithm
|
||||
*
|
||||
* @param {string} ciphertext String to be decrypted
|
||||
* @param {string} password Password to be used for decryption (1st 16 chars)
|
||||
* @returns {string} decrypted text
|
||||
*/
|
||||
Tea.decrypt = function(ciphertext, password) {
|
||||
if (ciphertext.length == 0) return('');
|
||||
var v = Tea.strToLongs(Base64.decode(ciphertext));
|
||||
var k = Tea.strToLongs(Utf8.encode(password).slice(0,16));
|
||||
var n = v.length;
|
||||
|
||||
// ---- <TEA decoding> ----
|
||||
|
||||
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
|
||||
var mx, e, q = Math.floor(6 + 52/n), sum = q*delta;
|
||||
|
||||
while (sum != 0) {
|
||||
e = sum>>>2 & 3;
|
||||
for (var p = n-1; p >= 0; p--) {
|
||||
z = v[p>0 ? p-1 : n-1];
|
||||
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
|
||||
y = v[p] -= mx;
|
||||
}
|
||||
sum -= delta;
|
||||
}
|
||||
|
||||
// ---- </TEA> ----
|
||||
|
||||
var plaintext = Tea.longsToStr(v);
|
||||
|
||||
// strip trailing null chars resulting from filling 4-char blocks:
|
||||
plaintext = plaintext.replace(/\0+$/,'');
|
||||
|
||||
return Utf8.decode(plaintext);
|
||||
}
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
// supporting functions
|
||||
|
||||
Tea.strToLongs = function(s) { // convert string to array of longs, each containing 4 chars
|
||||
// note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long
|
||||
var l = new Array(Math.ceil(s.length/4));
|
||||
for (var i=0; i<l.length; i++) {
|
||||
// note little-endian encoding - endianness is irrelevant as long as
|
||||
// it is the same in longsToStr()
|
||||
l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)<<8) +
|
||||
(s.charCodeAt(i*4+2)<<16) + (s.charCodeAt(i*4+3)<<24);
|
||||
}
|
||||
return l; // note running off the end of the string generates nulls since
|
||||
} // bitwise operators treat NaN as 0
|
||||
|
||||
Tea.longsToStr = function(l) { // convert array of longs back to string
|
||||
var a = new Array(l.length);
|
||||
for (var i=0; i<l.length; i++) {
|
||||
a[i] = String.fromCharCode(l[i] & 0xFF, l[i]>>>8 & 0xFF,
|
||||
l[i]>>>16 & 0xFF, l[i]>>>24 & 0xFF);
|
||||
}
|
||||
return a.join(''); // use Array.join() rather than repeated string appends for efficiency in IE
|
||||
}
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* Base64 class: Base 64 encoding / decoding (c) Chris Veness 2002-2010 */
|
||||
/* note: depends on Utf8 class */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
var Base64 = {}; // Base64 namespace
|
||||
|
||||
Base64.code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
/**
|
||||
* Encode string into Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648]
|
||||
* (instance method extending String object). As per RFC 4648, no newlines are added.
|
||||
*
|
||||
* @param {String} str The string to be encoded as base-64
|
||||
* @param {Boolean} [utf8encode=false] Flag to indicate whether str is Unicode string to be encoded
|
||||
* to UTF8 before conversion to base64; otherwise string is assumed to be 8-bit characters
|
||||
* @returns {String} Base64-encoded string
|
||||
*/
|
||||
Base64.encode = function(str, utf8encode) { // http://tools.ietf.org/html/rfc4648
|
||||
utf8encode = (typeof utf8encode == 'undefined') ? false : utf8encode;
|
||||
var o1, o2, o3, bits, h1, h2, h3, h4, e=[], pad = '', c, plain, coded;
|
||||
var b64 = Base64.code;
|
||||
|
||||
plain = utf8encode ? Utf8.encode(str) : str;
|
||||
|
||||
c = plain.length % 3; // pad string to length of multiple of 3
|
||||
if (c > 0) { while (c++ < 3) { pad += '='; plain += '\0'; } }
|
||||
// note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars
|
||||
|
||||
for (c=0; c<plain.length; c+=3) { // pack three octets into four hexets
|
||||
o1 = plain.charCodeAt(c);
|
||||
o2 = plain.charCodeAt(c+1);
|
||||
o3 = plain.charCodeAt(c+2);
|
||||
|
||||
bits = o1<<16 | o2<<8 | o3;
|
||||
|
||||
h1 = bits>>18 & 0x3f;
|
||||
h2 = bits>>12 & 0x3f;
|
||||
h3 = bits>>6 & 0x3f;
|
||||
h4 = bits & 0x3f;
|
||||
|
||||
// use hextets to index into code string
|
||||
e[c/3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
|
||||
}
|
||||
coded = e.join(''); // join() is far faster than repeated string concatenation in IE
|
||||
|
||||
// replace 'A's from padded nulls with '='s
|
||||
coded = coded.slice(0, coded.length-pad.length) + pad;
|
||||
|
||||
return coded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode string from Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648]
|
||||
* (instance method extending String object). As per RFC 4648, newlines are not catered for.
|
||||
*
|
||||
* @param {String} str The string to be decoded from base-64
|
||||
* @param {Boolean} [utf8decode=false] Flag to indicate whether str is Unicode string to be decoded
|
||||
* from UTF8 after conversion from base64
|
||||
* @returns {String} decoded string
|
||||
*/
|
||||
Base64.decode = function(str, utf8decode) {
|
||||
utf8decode = (typeof utf8decode == 'undefined') ? false : utf8decode;
|
||||
var o1, o2, o3, h1, h2, h3, h4, bits, d=[], plain, coded;
|
||||
var b64 = Base64.code;
|
||||
|
||||
coded = utf8decode ? Utf8.decode(str) : str;
|
||||
|
||||
|
||||
for (var c=0; c<coded.length; c+=4) { // unpack four hexets into three octets
|
||||
h1 = b64.indexOf(coded.charAt(c));
|
||||
h2 = b64.indexOf(coded.charAt(c+1));
|
||||
h3 = b64.indexOf(coded.charAt(c+2));
|
||||
h4 = b64.indexOf(coded.charAt(c+3));
|
||||
|
||||
bits = h1<<18 | h2<<12 | h3<<6 | h4;
|
||||
|
||||
o1 = bits>>>16 & 0xff;
|
||||
o2 = bits>>>8 & 0xff;
|
||||
o3 = bits & 0xff;
|
||||
|
||||
d[c/4] = String.fromCharCode(o1, o2, o3);
|
||||
// check for padding
|
||||
if (h4 == 0x40) d[c/4] = String.fromCharCode(o1, o2);
|
||||
if (h3 == 0x40) d[c/4] = String.fromCharCode(o1);
|
||||
}
|
||||
plain = d.join(''); // join() is far faster than repeated string concatenation in IE
|
||||
|
||||
return utf8decode ? Utf8.decode(plain) : plain;
|
||||
}
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* Utf8 class: encode / decode between multi-byte Unicode characters and UTF-8 multiple */
|
||||
/* single-byte character encoding (c) Chris Veness 2002-2010 */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
var Utf8 = {}; // Utf8 namespace
|
||||
|
||||
/**
|
||||
* Encode multi-byte Unicode string into utf-8 multiple single-byte characters
|
||||
* (BMP / basic multilingual plane only)
|
||||
*
|
||||
* Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars
|
||||
*
|
||||
* @param {String} strUni Unicode string to be encoded as UTF-8
|
||||
* @returns {String} encoded string
|
||||
*/
|
||||
Utf8.encode = function(strUni) {
|
||||
// use regular expressions & String.replace callback function for better efficiency
|
||||
// than procedural approaches
|
||||
var strUtf = strUni.replace(
|
||||
/[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz
|
||||
function(c) {
|
||||
var cc = c.charCodeAt(0);
|
||||
return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); }
|
||||
);
|
||||
strUtf = strUtf.replace(
|
||||
/[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz
|
||||
function(c) {
|
||||
var cc = c.charCodeAt(0);
|
||||
return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); }
|
||||
);
|
||||
return strUtf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode utf-8 encoded string back into multi-byte Unicode characters
|
||||
*
|
||||
* @param {String} strUtf UTF-8 string to be decoded back to Unicode
|
||||
* @returns {String} decoded string
|
||||
*/
|
||||
Utf8.decode = function(strUtf) {
|
||||
// note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char!
|
||||
var strUni = strUtf.replace(
|
||||
/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars
|
||||
function(c) { // (note parentheses for precence)
|
||||
var cc = ((c.charCodeAt(0)&0x0f)<<12) | ((c.charCodeAt(1)&0x3f)<<6) | ( c.charCodeAt(2)&0x3f);
|
||||
return String.fromCharCode(cc); }
|
||||
);
|
||||
strUni = strUni.replace(
|
||||
/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars
|
||||
function(c) { // (note parentheses for precence)
|
||||
var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f;
|
||||
return String.fromCharCode(cc); }
|
||||
);
|
||||
return strUni;
|
||||
}
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
Antes Largura: | Altura: | Tamanho: 868 B Depois Largura: | Altura: | Tamanho: 868 B |
|
Antes Largura: | Altura: | Tamanho: 814 B Depois Largura: | Altura: | Tamanho: 814 B |
|
Antes Largura: | Altura: | Tamanho: 853 B Depois Largura: | Altura: | Tamanho: 853 B |
|
Antes Largura: | Altura: | Tamanho: 853 B Depois Largura: | Altura: | Tamanho: 853 B |
|
Antes Largura: | Altura: | Tamanho: 850 B Depois Largura: | Altura: | Tamanho: 850 B |
|
Antes Largura: | Altura: | Tamanho: 843 B Depois Largura: | Altura: | Tamanho: 843 B |
|
Antes Largura: | Altura: | Tamanho: 860 B Depois Largura: | Altura: | Tamanho: 860 B |
|
Antes Largura: | Altura: | Tamanho: 846 B Depois Largura: | Altura: | Tamanho: 846 B |
|
Antes Largura: | Altura: | Tamanho: 854 B Depois Largura: | Altura: | Tamanho: 854 B |
@@ -1,4 +1,48 @@
|
||||
.imjs-default { display: none; }
|
||||
/*
|
||||
Content-Type: mutipart/related; boundary="_FOR_IE_6_AND_7"
|
||||
|
||||
--_FOR_IE_6_AND_7
|
||||
Content-Location:bottom
|
||||
Content-Transfer-Encoding:base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAW8AAAABCAIAAACe17jdAAAAF0lEQVQ4y2NwcXEdRaOIePRqFIwCHAAACPCDdjrG0LoAAAAASUVORK5CYII=
|
||||
--_FOR_IE_6_AND_7
|
||||
Content-Location:closemin
|
||||
Content-Transfer-Encoding:base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAZCAMAAAA/rk+6AAAABlBMVEUHAQUAAACy3FD1AAAAAnRSTlP/AOW3MEoAAAAwSURBVBjTY2CEAwZGBgYwzQBmMjCCCQgF4TJC2IzoTIQCJG1IhiFZMciYDFCAzAQAZEoAw+FFzSEAAAAASUVORK5CYII=
|
||||
--_FOR_IE_6_AND_7
|
||||
Content-Location:error
|
||||
Content-Transfer-Encoding:base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABpklEQVQ4y52RyUsCURzHhYJudegYBUV/REVFe7QdEtoOnVouXYKCDgZGJEEUeIqo6BB0qMAWPARWaoSHmjR1KltwurSIRTM6mjrBt/fGtKgw7fDhLfN+n/d981PsqdMVCUj74Mc3ADIJi/fHMkD5TZKMQMWutYFd76QCVaqCPJMmC4EnBgHvEeic7qUimOHMakjBS0iBM3CmUSrQJisoPZzORUS8RPBhCcG7WUg+FhZtIZXUJiNYubfOQfLbaYFMhD/Ao32eznV/CXqYxWK8iecQb8fjAv/1IEnBgFkoouv+RALmxb2NkFcH/83wp8DVh9DjMnhOF2/rbwIVu6qEJFjgc/XKRTGB76wLgrMVkZc9XGx2072J74IckyYT4oMBwdsJ+Nh2Qkd0dCohOFognNZDvBrAq2cX5slsKsn/Kphy7w4h/LxDDtZBsDcQGuMJBFs1eGs5+JMShJ+2wRlH5LbGBKRtOeTdBnJTMzlYAd5WSaiKjnRtLZOLKYKtBpFnI2lrAZU0Rdt2PE3MWwh7N5JkEx7HHBXo5QRUEoubAno5Qexv/pd3xf1YaSj1xLcAAAAASUVORK5CYII=
|
||||
--_FOR_IE_6_AND_7
|
||||
Content-Location:loading
|
||||
Content-Transfer-Encoding:base64
|
||||
|
||||
R0lGODlhEAAQAPQAAP///5iYmPv7+66urs3NzZqamqenp+3t7dvb26CgoMfHx8DAwPPz89XV1efn57S0tLq6ugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAAKAAEALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkEAAoAAgAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkEAAoAAwAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAAKAAQALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAAKAAUALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==
|
||||
--_FOR_IE_6_AND_7
|
||||
Content-Location:sl
|
||||
Content-Transfer-Encoding:base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQI12P4//8/AwizM7L+h7HhAiiCMAHCKrGZCQChyjk9tLLHDwAAAABJRU5ErkJggg==
|
||||
--_FOR_IE_6_AND_7
|
||||
Content-Location:sr
|
||||
Content-Transfer-Encoding:base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAIUlEQVQI12P4//8/AwizM7L+h7EZkAVhEhiCIExYJYwNAO9yOT1MGMFxAAAAAElFTkSuQmCC
|
||||
--_FOR_IE_6_AND_7
|
||||
Content-Location:tooltip
|
||||
Content-Transfer-Encoding:base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAA0AAAAICAYAAAAiJnXPAAAAiklEQVQY02PIzcrpZmdk/UoszkrP7Gf4//8/g4uD81agwH9CGKQOpB6s6fWrVzwq8krX8WkAyYPUwTWB8IXzF9QEuPheY9MAEgfJw9TCNYHwqhWr/KBuR9b0FSSOrA5FEwjX19RVIWn8CuKjq8HQBMIBPv4rQZpANDZ5rJo+fPjAFuwfuBxEY5MHACmH0K64j6daAAAAAElFTkSuQmCC
|
||||
--_FOR_IE_6_AND_7
|
||||
Content-Location:w
|
||||
Content-Transfer-Encoding:base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC
|
||||
--_FOR_IE_6_AND_7
|
||||
*/
|
||||
|
||||
/* [begin] Chatbox */
|
||||
.imjs-chatbox {
|
||||
@@ -48,7 +92,7 @@
|
||||
padding-top: 10px;
|
||||
width: 10px;
|
||||
overflow: hidden;
|
||||
background: url(images/closemin.png) 0 0 no-repeat;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAZCAMAAAA/rk+6AAAABlBMVEUHAQUAAACy3FD1AAAAAnRSTlP/AOW3MEoAAAAwSURBVBjTY2CEAwZGBgYwzQBmMjCCCQgF4TJC2IzoTIQCJG1IhiFZMciYDFCAzAQAZEoAw+FFzSEAAAAASUVORK5CYII=) 0 0 no-repeat;
|
||||
}
|
||||
|
||||
div.imjs-header a.imjs-minimize {
|
||||
@@ -59,7 +103,7 @@
|
||||
height: 0;
|
||||
width: 207px;
|
||||
overflow: hidden;
|
||||
background: url(images/closemin.png) 100% -10px no-repeat;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAZCAMAAAA/rk+6AAAABlBMVEUHAQUAAACy3FD1AAAAAnRSTlP/AOW3MEoAAAAwSURBVBjTY2CEAwZGBgYwzQBmMjCCCQgF4TJC2IzoTIQCJG1IhiFZMciYDFCAzAQAZEoAw+FFzSEAAAAASUVORK5CYII=) 100% -10px no-repeat;
|
||||
}
|
||||
|
||||
div.imjs-header a.imjs-minimize:active {
|
||||
@@ -98,18 +142,18 @@
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
li.imjs-msg-a, li.imjs-msg-b {
|
||||
li.imjs-msg-you, li.imjs-msg-them {
|
||||
display: block;
|
||||
padding: 3px 5px 3px 8px;
|
||||
border-top: 1px dotted #bfbfbf;
|
||||
}
|
||||
|
||||
li.imjs-msg-b > span {
|
||||
li.imjs-msg-them > span {
|
||||
color: #ff0000;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
li.imjs-msg-a > span {
|
||||
li.imjs-msg-you > span {
|
||||
color: #0099ff;
|
||||
font-weight: 700;
|
||||
}
|
||||
@@ -134,7 +178,7 @@
|
||||
color: #bcbcbc;
|
||||
}
|
||||
|
||||
li.imjs-msg-a ul p, li.imjs-msg-b ul p, li.imjs-date ul p {
|
||||
li.imjs-msg-you ul p, li.imjs-msg-them ul p, li.imjs-date ul p {
|
||||
margin: 0;
|
||||
}
|
||||
/* [end] Message Log Messages */
|
||||
@@ -149,6 +193,8 @@
|
||||
border: solid #aaa;
|
||||
border-width: 1px 0 0 0;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
outline: none;
|
||||
}
|
||||
/* [end] Input */
|
||||
/* [end] Chatbox */
|
||||
@@ -158,15 +204,14 @@ ul#imjs-bar {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 95%;
|
||||
background: #f0efed url(images/w.png) top left repeat-x;
|
||||
right: 5%;
|
||||
background: #f0efed url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC) top left repeat-x;
|
||||
height: 25px;
|
||||
border: solid #cfceca;
|
||||
border-width: 1px 1px 0 1px;
|
||||
border-width: 1px 1px 0 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0 0 0 -47.5%;
|
||||
margin: 0;
|
||||
font: 12px/25px Helvetica Neue, Helvetica, Arial, Calibri, Tahoma, Verdana, sans-serif;
|
||||
color: #222;
|
||||
}
|
||||
@@ -174,7 +219,7 @@ ul#imjs-bar {
|
||||
/* [begin] Generic bar item */
|
||||
ul#imjs-bar > li {
|
||||
position: relative;
|
||||
float: right;
|
||||
float: left;
|
||||
width: 161px;
|
||||
border-left: 1px solid #cecece;
|
||||
padding: 0 10px;
|
||||
@@ -190,7 +235,8 @@ ul#imjs-bar {
|
||||
position: relative;
|
||||
color: #00cc00;
|
||||
font-size: 28px;
|
||||
line-height: 19px;
|
||||
line-height: 13px;
|
||||
left: -1px;
|
||||
top: 6px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
@@ -231,14 +277,15 @@ ul#imjs-bar {
|
||||
border-color: #444;
|
||||
border-bottom: 0;
|
||||
padding-bottom: 1px;
|
||||
background-image: url(images/bottom.png);
|
||||
background-position: left bottom;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAW8AAAABCAIAAACe17jdAAAAF0lEQVQ4y2NwcXEdRaOIePRqFIwCHAAACPCDdjrG0LoAAAAASUVORK5CYII=);
|
||||
*background-image: url(mhtml:/theme.css!bottom);
|
||||
background-position: -142px bottom;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
ul#imjs-bar > li.imjs-selected > #imjs-friends-panel.imjs-chatbox {
|
||||
background-repeat: repeat-x;
|
||||
background-position: -45px bottom;
|
||||
background-position: -132px bottom;
|
||||
}
|
||||
|
||||
/* [begin] Generic bar close */
|
||||
@@ -251,7 +298,7 @@ ul#imjs-bar {
|
||||
padding-top: 10px;
|
||||
overflow: hidden;
|
||||
margin-top: 7.5px;
|
||||
background: url(images/closemin.png) 0 0 no-repeat;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAZCAMAAAA/rk+6AAAABlBMVEUHAQUAAACy3FD1AAAAAnRSTlP/AOW3MEoAAAAwSURBVBjTY2CEAwZGBgYwzQBmMjCCCQgF4TJC2IzoTIQCJG1IhiFZMciYDFCAzAQAZEoAw+FFzSEAAAAASUVORK5CYII=) 0 0 no-repeat;
|
||||
}
|
||||
|
||||
ul#imjs-bar > li:hover .imjs-tab-text a {
|
||||
@@ -271,14 +318,16 @@ ul#imjs-bar {
|
||||
|
||||
ul#imjs-bar li#imjs-scroll-left {
|
||||
padding: 1px 6px 0 4px;
|
||||
background-image: url(images/sl.png);
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQI12P4//8/AwizM7L+h7HhAiiCMAHCKrGZCQChyjk9tLLHDwAAAABJRU5ErkJggg==);
|
||||
background-position: 5px center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
ul#imjs-bar li#imjs-scroll-right {
|
||||
padding: 1px 5px 0;
|
||||
background-image: url(images/sr.png);
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAIUlEQVQI12P4//8/AwizM7L+h7EZkAVhEhiCIExYJYwNAO9yOT1MGMFxAAAAAElFTkSuQmCC);
|
||||
background-position: 20px center;
|
||||
float: right;
|
||||
}
|
||||
/* [end] Bar scrolling buttons */
|
||||
|
||||
@@ -310,7 +359,7 @@ ul#imjs-bar {
|
||||
padding-bottom: 5px;
|
||||
margin: 0 0 3px 6px;
|
||||
left: -10000px;
|
||||
background: url(images/tooltip.png) right bottom no-repeat;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAICAYAAAAiJnXPAAAAiklEQVQY02PIzcrpZmdk/UoszkrP7Gf4//8/g4uD81agwH9CGKQOpB6s6fWrVzwq8krX8WkAyYPUwTWB8IXzF9QEuPheY9MAEgfJw9TCNYHwqhWr/KBuR9b0FSSOrA5FEwjX19RVIWn8CuKjq8HQBMIBPv4rQZpANDZ5rJo+fPjAFuwfuBxEY5MHACmH0K64j6daAAAAAElFTkSuQmCC) right bottom no-repeat;
|
||||
color: #fff;
|
||||
text-shadow: none;
|
||||
opacity: 0.9;
|
||||
@@ -326,10 +375,15 @@ ul#imjs-bar {
|
||||
/* [end] Tooltip */
|
||||
|
||||
/* [begin] Friends list button */
|
||||
ul#imjs-bar > li#imjs-friends {
|
||||
float: right;
|
||||
width: 125px;
|
||||
}
|
||||
|
||||
ul#imjs-bar > li#imjs-friends.imjs-not-connected,
|
||||
ul#imjs-bar > li#imjs-friends.not-connected:hover {
|
||||
ul#imjs-bar > li#imjs-friends.imjs-not-connected:hover {
|
||||
width: 10px;
|
||||
background-image: url(images/error.png);
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABpklEQVQ4y52RyUsCURzHhYJudegYBUV/REVFe7QdEtoOnVouXYKCDgZGJEEUeIqo6BB0qMAWPARWaoSHmjR1KltwurSIRTM6mjrBt/fGtKgw7fDhLfN+n/d981PsqdMVCUj74Mc3ADIJi/fHMkD5TZKMQMWutYFd76QCVaqCPJMmC4EnBgHvEeic7qUimOHMakjBS0iBM3CmUSrQJisoPZzORUS8RPBhCcG7WUg+FhZtIZXUJiNYubfOQfLbaYFMhD/Ao32eznV/CXqYxWK8iecQb8fjAv/1IEnBgFkoouv+RALmxb2NkFcH/83wp8DVh9DjMnhOF2/rbwIVu6qEJFjgc/XKRTGB76wLgrMVkZc9XGx2072J74IckyYT4oMBwdsJ+Nh2Qkd0dCohOFognNZDvBrAq2cX5slsKsn/Kphy7w4h/LxDDtZBsDcQGuMJBFs1eGs5+JMShJ+2wRlH5LbGBKRtOeTdBnJTMzlYAd5WSaiKjnRtLZOLKYKtBpFnI2lrAZU0Rdt2PE3MWwh7N5JkEx7HHBXo5QRUEoubAno5Qexv/pd3xf1YaSj1xLcAAAAASUVORK5CYII=);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
@@ -347,14 +401,111 @@ ul#imjs-bar {
|
||||
font-weight: 700;
|
||||
}
|
||||
/* [end] Friends list button */
|
||||
|
||||
/* [begin] Reconnect button */
|
||||
ul#imjs-bar > li#imjs-reconnect {
|
||||
display: none;
|
||||
float: right;
|
||||
width: 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
ul#imjs-bar > li#imjs-reconnect span {
|
||||
margin-left: -3px;
|
||||
}
|
||||
/* [end] Reconnect button */
|
||||
/* [end] Messenger bar */
|
||||
|
||||
/* [begin] Status panel */
|
||||
div#imjs-status-panel {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
textarea#imjs-away-message-text {
|
||||
display: none;
|
||||
resize: none;
|
||||
width: 169px;
|
||||
height: 30px;
|
||||
margin: 5px 9px 0;
|
||||
padding: 5px;
|
||||
border: 1px solid #cfceca;
|
||||
font: 11px/17px Helvetica Neue, Helvetica, Arial, Calibri, Tahoma, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
textarea#imjs-away-message-text.imjs-loading {
|
||||
background: url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///5iYmPv7+66urs3NzZqamqenp+3t7dvb26CgoMfHx8DAwPPz89XV1efn57S0tLq6ugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAAKAAEALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkEAAoAAgAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkEAAoAAwAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAAKAAQALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAAKAAUALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==) 161px 2px no-repeat;
|
||||
}
|
||||
|
||||
div#imjs-away-message-text-arrow {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: -3px 0 0 87px;
|
||||
border-style: solid;
|
||||
border-width: 4px 4px 0;
|
||||
border-color: #cfceca transparent transparent transparent;
|
||||
}
|
||||
|
||||
textarea#imjs-away-message-text:focus {
|
||||
outline: 2px solid #cfceca;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a.imjs-button {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
padding: 0 7px;
|
||||
font-size: 10px;
|
||||
margin: 5px 0;
|
||||
-moz-border-radius: 10px;
|
||||
-webkit-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a.imjs-button span {
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
margin: 1px 3px 0 0;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a.imjs-button.imjs-toggled {
|
||||
background-color: #e0e0e0;
|
||||
border-left-color: #c0c0c0;
|
||||
border-top-color: #c0c0c0;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a.imjs-button.imjs-toggled:hover {
|
||||
background-color: #d7d7d7;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a.imjs-button:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a#imjs-button-away span {
|
||||
color: #df9b00;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a#imjs-button-online {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a#imjs-button-online span {
|
||||
color: #00cc00;
|
||||
}
|
||||
|
||||
div#imjs-status-panel > a#imjs-button-offline span {
|
||||
color: #777;
|
||||
}
|
||||
/* [end] Status panel */
|
||||
|
||||
/* [begin] Friends list */
|
||||
div#imjs-friends-panel {
|
||||
background: #fff;
|
||||
width: 200px;
|
||||
right: auto;
|
||||
left: -1px;
|
||||
right: -1px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@@ -411,6 +562,10 @@ ul#imjs-friends-list {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
li.imjs-friend-group ul li:hover span.imjs-friend-status {
|
||||
color: #5184e0;
|
||||
}
|
||||
|
||||
li.imjs-friend-group ul li > strong {
|
||||
position: relative;
|
||||
color: #00cc00;
|
||||
@@ -435,12 +590,19 @@ ul#imjs-friends-list {
|
||||
text-shadow: 0 1px 0 #333;
|
||||
}
|
||||
|
||||
span.imjs-friend-status {
|
||||
color: #888;
|
||||
white-space: nowrap;
|
||||
padding: 0 0 0 5px;
|
||||
}
|
||||
|
||||
div#imjs-friends-panel form, div#imjs-friends-panel form p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input#imjs-search-friends {
|
||||
display: none; /* Search does not work yet. */
|
||||
font: 12px/14px Helvetica Neue, Helvetica, Arial, Calibri, Tahoma, Verdana, sans-serif;
|
||||
height: 16px;
|
||||
width: 194px;
|
||||
@@ -451,85 +613,3 @@ div#imjs-friends-panel form, div#imjs-friends-panel form p {
|
||||
color: #aaa;
|
||||
}
|
||||
/* [end] Friends list */
|
||||
|
||||
/* [begin] Login/Registration Forms */
|
||||
div#imjs-lr form {
|
||||
background: #F0EFED url(images/w.png) repeat-x;
|
||||
border: 1px solid #cfceca;
|
||||
max-width: 220px;
|
||||
padding: 5px 5px 10px 8px;
|
||||
overflow: auto;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
}
|
||||
|
||||
div#imjs-lr .error {
|
||||
font: 700 12px/18px Helvetica Neue, Helvetica, Arial, Calibri, Tahoma, Verdana, sans-serif;
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
div#imjs-lr form fieldset {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div#imjs-lr form h2 {
|
||||
font: 700 20px/24px Helvetica Neue, Helvetica, Arial, Calibri, Tahoma, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 7px 0 0 2px;
|
||||
color: #555;
|
||||
text-shadow: 0 1px 0px #fff;
|
||||
}
|
||||
|
||||
div#imjs-lr form label {
|
||||
display: block;
|
||||
font: 700 12px/14px Helvetica Neue, Helvetica, Arial, Calibri, Tahoma, Verdana, sans-serif;
|
||||
color: #7B7A79;
|
||||
}
|
||||
|
||||
div#imjs-lr form input {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
div#imjs-lr form input.imjs-lr-error, div#imjs-lr form input.imjs-lr-error:focus {
|
||||
margin: -1px;
|
||||
border: 2px solid #ff1111;
|
||||
}
|
||||
|
||||
div#imjs-lr form button {
|
||||
float: right;
|
||||
background: #f4f3f1;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
div#imjs-lr form input, div#imjs-lr form button {
|
||||
border: 1px solid #ddd;
|
||||
color: #000;
|
||||
padding: 4px 3px;
|
||||
font: 16px/20px Helvetica Neue, Helvetica, Arial, Calibri, Tahoma, Verdana, sans-serif;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div#imjs-lr form p {
|
||||
padding-left: 2px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
div#imjs-lr form input:focus, div#imjs-lr form button:focus {
|
||||
position: relative;
|
||||
border: 2px solid #999;
|
||||
margin: -1px;
|
||||
}
|
||||
|
||||
p.imjs-submit {
|
||||
width: 207px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
form#imjs-login {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
/* [end] Login/Registration Forms */
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
// The backend engine library.
|
||||
define('DB_ENGINE', 'MySQL');
|
||||
|
||||
// The IM server library.
|
||||
define('IM_LIBRARY', 'NodeJS');
|
||||
|
||||
// If you're using the default MySQL server, change the below to
|
||||
// match your settings.
|
||||
define('MYSQL_USERNAME', 'root');
|
||||
define('MYSQL_PASSWORD', 'root');
|
||||
define('MYSQL_HOSTNAME', 'localhost');
|
||||
define('MYSQL_DATABASE', 'ajaxim');
|
||||
define('MYSQL_PREFIX', 'ajaxim_');
|
||||
|
||||
// Session cookie used by Ajax IM
|
||||
define('COOKIE_NAME', 'ajaxim_session');
|
||||
|
||||
// Cookie period, in days
|
||||
define('COOKIE_PERIOD', 365);
|
||||
|
||||
// Cookie domain (e.g.: .domain.com, if you want it to work for
|
||||
// all domains and subdomains), if any.
|
||||
define('COOKIE_DOMAIN', false);
|
||||
|
||||
// If you're using the NodeJS server, change the below to match
|
||||
// your settings.
|
||||
$nodejs_memcache_server = array('127.0.0.1', 11998);
|
||||
?>
|
||||
|
Antes Largura: | Altura: | Tamanho: 1.1 KiB |
@@ -1,573 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Ajax IM — Instant Messaging Framework</title>
|
||||
<script type="text/javascript" src="js/jquery-1.3.2.js"></script>
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #fff;
|
||||
font: 62.5% Helvetica Neue, Helvetica, Arial, Tahoma, sans-serif;
|
||||
}
|
||||
|
||||
input, button, select {
|
||||
font: 1.25em/1.25em Helvetica Neue, Helvetica, Arial, Tahoma, sans-serif;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 1.1em;
|
||||
font-family: Consolas, Inconsolata, Courier New, Courier, monospace;
|
||||
}
|
||||
|
||||
/* [begin] Upper */
|
||||
div.upper {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 1em 0;
|
||||
margin: 0 0 2em;
|
||||
background: #444;
|
||||
color: #fff;
|
||||
}
|
||||
/* [end] Upper */
|
||||
|
||||
/* [begin] Header */
|
||||
div#header {
|
||||
display: block;
|
||||
margin: 0 0 0 6.67em;
|
||||
font-size: 1.5em;
|
||||
line-height: 1em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div#header a {
|
||||
font-weight: 700;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-indent: -10000px;
|
||||
/*background: url(http://ajaxim.com/images/logo.png?install);*/
|
||||
background: url(site/demo/images/logo.png);
|
||||
width: 146px;
|
||||
height: 44px;
|
||||
margin: 0 0.5em 0 0;
|
||||
padding: 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.2em;
|
||||
line-height: 41px;
|
||||
}
|
||||
/* [end] Header */
|
||||
|
||||
/* [begin] Install form */
|
||||
form {
|
||||
font-size: 1.5em;
|
||||
margin-left: 6.67em;
|
||||
}
|
||||
|
||||
form fieldset {
|
||||
position: relative;
|
||||
border: 0;
|
||||
font-size: 1em;
|
||||
padding: 0 0 0.5em;
|
||||
margin: 0 0 2em;
|
||||
background: #eee;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
form fieldset legend {
|
||||
background: #eee;
|
||||
padding: 0.6em 1em 1em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
form fieldset p {
|
||||
margin: 0;
|
||||
padding: 0 1em 1.25em 1em;
|
||||
}
|
||||
|
||||
form fieldset p label {
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
form fieldset p span:first-child {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
form fieldset p span.desc {
|
||||
display: block;
|
||||
font-weight: 200;
|
||||
font-size: 0.8em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
input#install_db + label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
|
||||
form fieldset ul li {
|
||||
margin: 1em 0 0;
|
||||
}
|
||||
|
||||
form fieldset ul li p {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
form fieldset ul li pre {
|
||||
border-left: 2px solid #dedede;
|
||||
padding-left: 1em;
|
||||
margin-left: -1em;
|
||||
width: 540px;
|
||||
}
|
||||
|
||||
fieldset#mysql-config, fieldset#nodejs-config, p#please-install-db {
|
||||
display: none;
|
||||
}
|
||||
|
||||
form h3 {
|
||||
font-size: 2em;
|
||||
margin: 0 0 0.5em;
|
||||
}
|
||||
|
||||
p#install {
|
||||
text-align: right;
|
||||
width: 600px;
|
||||
margin: 0 0 3em;
|
||||
}
|
||||
|
||||
p#install input {
|
||||
font-size: 2em;
|
||||
}
|
||||
/* [end] Install form */
|
||||
|
||||
/* [begin] Footer */
|
||||
p#footer {
|
||||
font-size: 1.2em;
|
||||
color: #666;
|
||||
margin-left: 8.307em;
|
||||
}
|
||||
|
||||
p#footer a {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
p#footer a:visited {
|
||||
color: #666;
|
||||
}
|
||||
/* [end] Footer */
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('#server, #db').click();
|
||||
|
||||
$('#cookie_length, #session_length').change(function() {
|
||||
if(parseFloat($('#session_length').val()) < parseFloat($('#cookie_length').val())) {
|
||||
$('#session_length').val($('#cookie_length').val());
|
||||
}
|
||||
}).keypress();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="upper">
|
||||
<div id="header">
|
||||
<h1>Ajax IM</h1>
|
||||
<h2>instant messaging framework</h2>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// file_array() by Jamon Holmgren. Exclude files by putting them in the $exclude
|
||||
// string separated by pipes. Returns an array with filenames as strings.
|
||||
function file_array($path, $exclude = ".|..", $recursive = false) {
|
||||
$path = rtrim($path, "/") . "/";
|
||||
$folder_handle = opendir($path);
|
||||
$exclude_array = explode("|", $exclude);
|
||||
$result = array();
|
||||
while(false !== ($filename = readdir($folder_handle))) {
|
||||
if(!in_array(strtolower($filename), $exclude_array)) {
|
||||
if(is_dir($path . $filename . "/")) {
|
||||
if($recursive) $result[] = file_array($path, $exclude, true);
|
||||
} else {
|
||||
$result[] = $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function library_array($path, $exclude, $recursive = false) {
|
||||
$files = file_array($path, $exclude, $recursive);
|
||||
$libs = array();
|
||||
foreach($files as $f) {
|
||||
$library_src = preg_replace('/[\n\r]+/', "\n", file_get_contents(rtrim($path, "/") . "/" . $f));
|
||||
preg_match('/^<' . '\?php[^\/]*\/\*(.+?)\*\//sm', $library_src, $library_header);
|
||||
|
||||
$header_data = array(
|
||||
'Library' => $f,
|
||||
'Author' => 'Unknown',
|
||||
'Version' => '1.0',
|
||||
'Date' => '',
|
||||
'Description' => '',
|
||||
'Requirements' => array()
|
||||
);
|
||||
|
||||
if(strlen($library_header[1])) {
|
||||
$header_keys = array('Library', 'Author', 'Version', 'Date', 'Description', 'Requirements');
|
||||
$header_lines = explode("\n", $library_header[1]);
|
||||
|
||||
$last_key = false;
|
||||
foreach($header_lines as $ln) {
|
||||
$ln_kv = explode(':', substr($ln, 3), 2);
|
||||
$key = ucwords(strtolower($ln_kv[0]));
|
||||
if(in_array($key, $header_keys)) {
|
||||
$header_data[$ln_kv[0]] = trim($ln_kv[1]);
|
||||
$last_key = $key;
|
||||
} else if($last_key && strlen(trim($ln)) > 0) {
|
||||
$header_data[$last_key] .= substr($ln, 3) . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen($header_data['Requirements'])) {
|
||||
$reqs = explode(',', $header_data['Requirements']);
|
||||
foreach($reqs as &$req) $req = trim($req);
|
||||
$header_data['Requirements'] = $reqs;
|
||||
}
|
||||
}
|
||||
|
||||
$libs[$f] = $header_data;
|
||||
}
|
||||
|
||||
return $libs;
|
||||
}
|
||||
|
||||
function get_available_servers() {
|
||||
return library_array('./libraries/server', '.|..|base.php');
|
||||
}
|
||||
|
||||
function get_available_db_engines() {
|
||||
return library_array('./libraries/db', '.|..|base.php');
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$configphp = file_get_contents('config.php');
|
||||
|
||||
// Replace the server type
|
||||
$configphp = preg_replace("/define\('(IM_LIBRARY)', '[^']+?'\);/", "define('$1', '" . str_replace('.php', '', $_POST['server']) . "');", $configphp);
|
||||
|
||||
// Replace the database type
|
||||
$configphp = preg_replace("/define\('(DB_ENGINE)', '[^']+?'\);/", "define('$1', '" . str_replace('.php', '', $_POST['db']) . "');", $configphp);
|
||||
|
||||
// Replace the cookie name
|
||||
$configphp = preg_replace("/define\('(COOKIE_NAME)', '[^']+?'\);/", "define('$1', '" . $_POST['cookie'] . "');", $configphp);
|
||||
|
||||
// Replace the cookie storage length
|
||||
$configphp = preg_replace("/define\('(COOKIE_PERIOD)', [^)]+?\);/", "define('$1', '" . $_POST['cookie_length'] . "');", $configphp);
|
||||
|
||||
// Replace the MySQL information
|
||||
$configphp = preg_replace("/define\('(MYSQL_DATABASE)', '[^']+?'\);/", "define('$1', '" . $_POST['mysqldb'] . "');", $configphp);
|
||||
$configphp = preg_replace("/define\('(MYSQL_HOSTNAME)', '[^']+?'\);/", "define('$1', '" . $_POST['mysqlhost'] . "');", $configphp);
|
||||
$configphp = preg_replace("/define\('(MYSQL_USERNAME)', '[^']+?'\);/", "define('$1', '" . $_POST['mysqluser'] . "');", $configphp);
|
||||
$configphp = preg_replace("/define\('(MYSQL_PASSWORD)', '[^']+?'\);/", "define('$1', '" . $_POST['mysqlpass'] . "');", $configphp);
|
||||
$configphp = preg_replace("/define\('(MYSQL_PREFIX)', '[^']+?'\);/", "define('$1', '" . $_POST['mysqlprefix'] . "');", $configphp);
|
||||
|
||||
if(intval($_POST['needs_nodejs']) == 1) {
|
||||
$configjs = preg_replace("/[\r\n]+/", "\n", file_get_contents('server/config.js'));
|
||||
|
||||
// Replace the cookie name
|
||||
$configjs = preg_replace("/(name: )'[^']+?',/", "$1'" . $_POST['cookie'] . "',", $configjs);
|
||||
|
||||
// Replace the session period
|
||||
$configjs = preg_replace("/(period: )\d{1,6}/", "period: " . round(floatval($_POST['session_length']) * 24), $configjs);
|
||||
|
||||
// Replace the host and port numbers
|
||||
$public_host = strlen(trim($_POST['public_host'])) ? "'" . trim($_POST['public_host']) . "'" : "null";
|
||||
$private_host = strlen(trim($_POST['private_host'])) ? "'" . trim($_POST['private_host']) . "'" : "null";
|
||||
$configjs = preg_replace("/(public: )\[8000, 'localhost'\],/", "$1[" . $_POST['public_port'] . ", " . $public_host . "],", $configjs);
|
||||
$configjs = preg_replace("/(private: )\[11998, 'localhost'\]/", "$1[" . $_POST['private_port'] . ", " . $private_host . "],", $configjs);
|
||||
|
||||
$cjs = fopen('server/config.js', 'w');
|
||||
fwrite($cjs, $configjs);
|
||||
fclose($cjs);
|
||||
|
||||
$configphp = preg_replace('/(\$nodejs_memcache_server = array)\(\'[^\']+\', [^)]+\);/', "$1('" . ($private_host == 'null' ? 'localhost' : $_POST['private_host']) . "', " . $_POST['private_port'] . ");", $configphp);
|
||||
|
||||
if(!strlen(trim($_POST['public_host']))) {
|
||||
$url = parse_url($_POST['url']);
|
||||
$_POST['public_host'] = $url['host'];
|
||||
}
|
||||
|
||||
$node_url = $_POST['public_host'] . ":" . $_POST['public_port'];
|
||||
|
||||
$imloadjs = file_get_contents('js/im.load.js');
|
||||
$imloadjs = preg_replace("/(var nodehost = )'';/", "$1'" . $node_url . "';", $imloadjs);
|
||||
$imljs = fopen('js/im.load.js', 'w');
|
||||
fwrite($imljs, $imloadjs);
|
||||
fclose($imljs);
|
||||
}
|
||||
|
||||
$cphp = fopen('config.php', 'w');
|
||||
fwrite($cphp, $configphp);
|
||||
fclose($cphp);
|
||||
|
||||
if(isset($_POST['install_db'])) {
|
||||
// Get DB src from file, run against database
|
||||
$db_library_src = preg_replace("/[\r\n]+/", "\n", file_get_contents('libraries/db/MySQL.php'));
|
||||
preg_match('/\/\*\s+SQL:(.+?)\*\//sm', $db_library_src, $db_sql);
|
||||
$db_sql = explode(';', str_replace("\n", '', $db_sql[1]));
|
||||
|
||||
$sql = new PDO('mysql:dbname=' . $_POST['mysqldb'] . ';host=' . $_POST['mysqlhost'], $_POST['mysqluser'], $_POST['mysqlpass']);
|
||||
foreach($db_sql as $statement) {
|
||||
$sql->exec(str_replace('ajaxim_', $_POST['mysqlprefix'], $statement));
|
||||
}
|
||||
}
|
||||
?>
|
||||
<form method="post">
|
||||
<h3>Installation</h3>
|
||||
|
||||
<fieldset>
|
||||
<legend>Installation has completed successfully!</legend>
|
||||
<ul>
|
||||
<li>For security purposes, please delete <code>install.php</code>.</li>
|
||||
|
||||
<?php if(!$_POST['need_nodejs'] == 1) { ?>
|
||||
<li>The Node.js server host has been added to <code>js/im.load.js</code>; please confirm that the URL generated is correct.</li>
|
||||
<li>
|
||||
<p>To get the standalone Node.js server running, you will need to run the following command on the command line of your server:</p>
|
||||
<pre>node server/server.js</pre>
|
||||
|
||||
<p>However, it is recommended that you move the <code>server</code> folder outside of the main installation directory before going live.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Now that Ajax IM is installed, please see the <a href="http://ajaxim.com/documentation/basics/quick-start-nodejs/">Quick Start with Node.js</a> guide to complete set up.</p>
|
||||
</li>
|
||||
<?php } else { ?>
|
||||
<li>Since you did <strong>not</strong> setup Ajax IM with the Node.js server, it is recommended that you delete the <code>server</code> folder (containing the files <code>config.js</code> and <code>server.js</code>).</li>
|
||||
<li>
|
||||
<p>Now that Ajax IM is installed, please see the <a href="http://ajaxim.com/documentation/basics/quick-start/">Quick Start</a> guide to complete set up.</p>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</fieldset>
|
||||
</form>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<form method="post">
|
||||
<h3>Installation</h3>
|
||||
|
||||
<p>For assistance, please see the <a href="http://ajaxim.com/installation/">installation tutorial</a>.</p>
|
||||
|
||||
<fieldset>
|
||||
<legend>Basic Configuration</legend>
|
||||
|
||||
<p>
|
||||
<span>Is <code>config.php</code> writable?</span>
|
||||
<?php if(is_writable('config.php')): ?>
|
||||
<span class="writable">Yes.</span>
|
||||
<?php else: ?>
|
||||
<span class="problem"><strong>No.</strong> Please change the permissions of <code>config.php</code> to "writable" or 755 (on Unix-based systems) before continuing!</span>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<span>Is <code>js/im.load.js</code> writable?</span>
|
||||
<?php if(is_writable('js/im.load.js')): ?>
|
||||
<span class="writable">Yes.</span>
|
||||
<?php else: ?>
|
||||
<span class="problem"><strong>No.</strong> Please change the permissions of <code>js/im.load.js</code> to "writable" or 755 (on Unix-based systems) before continuing!</span>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="url">What is the URL of the installation?</label>
|
||||
<input type="text" size="50" name="url" id="url" value="<?php print ($_SERVER['HTTPS'] ? 'https' : 'http') . '://' . $_SERVER['SERVER_NAME'] . str_replace('install.php', '', $_SERVER['REQUEST_URI']); ?>">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="server">Please select a server type.</label>
|
||||
<select name="server" id="server">
|
||||
<?php $servers = get_available_servers();
|
||||
foreach($servers as $file => $server):
|
||||
?>
|
||||
<option value="<?php print $file; ?>"><?php print $server['Library'] . ' — ' . $server['Version']; ?> (by <?php print $server['Author']; ?>)</option>
|
||||
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input type="hidden" name="needs_nodejs" id="needs_nodejs" value="0">
|
||||
<script type="text/javascript">
|
||||
var servers = <?php print json_encode($servers); ?>;
|
||||
servers['None'] = {Description: 'None', Requirements: ['None']};
|
||||
var selected_server = servers['None'];
|
||||
var update_server_list = function(e) {
|
||||
if(!(server = $('#server').val()))
|
||||
server = 'None';
|
||||
|
||||
$('#server-select .description').text(servers[server].Description);
|
||||
$('#server-select .requirements').text(servers[server].Requirements.join(', '));
|
||||
|
||||
selected_server = servers[server];
|
||||
};
|
||||
$('#server').live('click', update_server_list).live('change', update_server_list);
|
||||
</script>
|
||||
</p>
|
||||
|
||||
<p id="server-select">
|
||||
<em>Description:</em> <span class="description">None</span><br>
|
||||
<em>Requirements:</em> <span class="requirements">None</span>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="db">If you will be using a database engine, please select one.</label>
|
||||
<select name="db" id="db">
|
||||
<option value="">No database engine selected</option>
|
||||
<?php $dbs = get_available_db_engines();
|
||||
foreach($dbs as $file => $db):
|
||||
?>
|
||||
<option value="<?php print $file; ?>"><?php print $db['Library'] . ' — ' . $db['Version']; ?> (by <?php print $db['Author']; ?>)</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<script type="text/javascript">
|
||||
var db_engines = <?php print json_encode($dbs); ?>;
|
||||
var engine = 'None';
|
||||
db_engines['None'] = {Description: 'None', Requirements: ['None']};
|
||||
var update_db_list = function(e) {
|
||||
if(!(engine = $('#db').val()))
|
||||
engine = 'None';
|
||||
|
||||
$('#db-select .description').text(db_engines[engine].Description);
|
||||
$('#db-select .requirements').text(db_engines[engine].Requirements.join(', '));
|
||||
};
|
||||
$('#db').live('click', update_db_list).live('change', update_db_list);
|
||||
|
||||
var update_reqs = function() {
|
||||
setTimeout(function() {
|
||||
if(engine == 'MySQL.php' && $.inArray('Database', selected_server.Requirements) != -1) {
|
||||
$('#please-install-db').fadeIn();
|
||||
$('#mysql-config').slideDown();
|
||||
} else {
|
||||
$('#please-install-db').fadeOut();
|
||||
$('#mysql-config').slideUp();
|
||||
}
|
||||
|
||||
if($.inArray('Node.js', selected_server.Requirements) != -1) {
|
||||
$('#nodejs-config').slideDown();
|
||||
$('#needs_nodejs').val('1');
|
||||
} else {
|
||||
$('#nodejs-config').slideUp();
|
||||
$('#needs_nodejs').val('0');
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
$('#db, #server').live('click', update_reqs).live('change', update_reqs);
|
||||
</script>
|
||||
</p>
|
||||
|
||||
<p id="db-select">
|
||||
<em>Description:</em> <span class="description">None</span><br>
|
||||
<em>Requirements:</em> <span class="requirements">None</span>
|
||||
</p>
|
||||
|
||||
<p id="please-install-db">
|
||||
<input type="checkbox" name="install_db" id="install_db" value="yes" checked="checked">
|
||||
<label for="install_db">Please install the database for me.</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="cookie">Cookie name</label>
|
||||
<span class="desc">Ajax IM uses a cookie to keep track of the authenticated user's session. You can set the cookie name and session storage length.</span>
|
||||
<input type="text" name="cookie" id="cookie" value="ajaxim_session">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="cookie_length">Cookie storage length</label>
|
||||
<span class="desc">The number of days for which to store the cookie. Setting it to 0 makes it a session cookie (deleted when the browser is closed). If Ajax IM is integrated with another authentication system, this cookie should be kept as long or longer than that cookie.</span>
|
||||
<input type="text" maxlength="4" size="4" name="cookie_length" id="cookie_length" value="365"> days
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="mysql-config">
|
||||
<legend>MySQL Configuration</legend>
|
||||
|
||||
<p>
|
||||
<label for="mysqldb">Database</label>
|
||||
<input type="text" name="mysqldb" id="mysqldb">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="mysqlhost">Hostname</label>
|
||||
<span class="desc">Note: Usually, you will not need to change this value.</span>
|
||||
<input type="text" name="mysqlhost" id="mysqlhost" value="localhost">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="mysqluser">Username</label>
|
||||
<input type="text" name="mysqluser" id="mysqluser">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="mysqlpass">Password</label>
|
||||
<input type="text" name="mysqlpass" id="mysqlpass">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="mysqluser">Table Prefix</label>
|
||||
<input type="text" name="mysqlprefix" id="mysqlprefix" value="ajaxim_">
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="nodejs-config">
|
||||
<legend>Standalone (Node.js) Server Configuration</legend>
|
||||
|
||||
<p>After configuration is complete, please see the <a href="http://ajaxim.com/installation">installation tutorial</a> to complete the standalone server setup.</p>
|
||||
|
||||
<p>
|
||||
<span>Is <code>server/config.js</code> writable?</span>
|
||||
<?php if(is_writable('server/config.js')) { ?>
|
||||
<span class="writable">Yes.</span>
|
||||
<?php } else { ?>
|
||||
<span class="problem"><strong>No.</strong> Please change the permissions of <code>server/config.js</code> to "writable" or 755 (on Unix-based systems) before continuing!</span>
|
||||
<?php } ?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="public_port">Public host and port</label>
|
||||
<span class="desc">Host is optional; set to blank for none. This is the port number that the client will use to communicate with the server.</span>
|
||||
<input type="text" size="15" name="public_host" id="public_host" value="localhost"> :
|
||||
<input type="text" maxlength="5" size="5" name="public_port" id="public_port" value="31411">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="public_port">Private (API) host and port</label>
|
||||
<span class="desc">Host is optional; set to blank for none. This is the port number that the <code>server.php</code> script will use, as well as the port you will use for the <a href="http://ajaxim.com/documentation/api">Memcache-based API</a>.</span>
|
||||
<input type="text" size="15" name="private_host" id="private_host" value="localhost"> :
|
||||
<input type="text" maxlength="5" size="5" name="private_port" id="private_port" value="11211">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="session_length">Session storage length</label>
|
||||
<span class="desc">The number of days for which to store the session — which is different than the cookie. The cookie holds on to the session identifier, but if the session doesn't exist any longer, the cookie is useless. Therefore, the session needs to be kept as long, or longer, than the cookie. Minimum 0.042 days (approx. one hour).</span>
|
||||
<input type="text" maxlength="4" size="4" name="session_length" id="session_length" value="365"> days
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<p id="install"><input type="submit" value="Install" name="install"></p>
|
||||
</form>
|
||||
<?php } ?>
|
||||
|
||||
<p id="footer"><a href="http://ajaxim.com">Ajax IM</a> © 2005 – 2010, <a href="http://unwieldy.net">Joshua Gross</a></p></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,79 +0,0 @@
|
||||
// Automatically load dependencies, in order, if they aren't already loaded.
|
||||
// Each array is: [filename, deptest] where deptest is the function to
|
||||
// test for the dependency.
|
||||
var AjaxIM, AjaxIMLoadedFunction;
|
||||
(function() {
|
||||
AjaxIM = {};
|
||||
AjaxIM.loaded = function(f) {
|
||||
AjaxIMLoadedFunction = f;
|
||||
};
|
||||
|
||||
var tagsrc =
|
||||
(thistag = document.getElementsByTagName('script'))[thistag.length-1].src;
|
||||
var jsfolder = tagsrc.replace(/im.load.js([?].+)?/, '');
|
||||
var imfolder = jsfolder.replace(/js\/$/, '');
|
||||
|
||||
var nodehost = '';
|
||||
|
||||
var dependencies = [
|
||||
['jquery-1.3.2.js', function() { return (typeof window['jQuery'] != 'undefined'); }],
|
||||
['jquery.jsonp-1.1.0.js', function() { return (typeof jQuery['jsonp'] != 'undefined'); }],
|
||||
['jquery.jstore-all-min.js', function() { return (typeof jQuery['jstore'] != 'undefined'); }],
|
||||
['jquery.md5.js', function() { return (typeof jQuery['md5'] != 'undefined'); }],
|
||||
['im.js', function() { return (typeof window['AjaxIM'] != 'object'); }]
|
||||
];
|
||||
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
|
||||
(loadDep = function(depPos) {
|
||||
if(depPos >= dependencies.length) { init(); return; }
|
||||
var dep = dependencies[depPos];
|
||||
|
||||
if(!dep[1]()) {
|
||||
var newdep = document.createElement('script');
|
||||
newdep.type = 'text/javascript';
|
||||
newdep.src = jsfolder + dep[0];
|
||||
|
||||
var nextdep = function() { loadDep(depPos + 1); };
|
||||
newdep.onload = nextdep;
|
||||
newdep.onreadystatechange = nextdep;
|
||||
|
||||
head.appendChild(newdep);
|
||||
} else loadDep(depPos + 1);
|
||||
})(0);
|
||||
|
||||
var init = function() {
|
||||
if(tagsrc.match(/[?]php$/)) {
|
||||
AjaxIM.init({
|
||||
pollServer: imfolder + 'ajaxim.php',
|
||||
theme: imfolder + 'themes/default',
|
||||
flashStorage: jsfolder + 'jStore.Flash.html'
|
||||
});
|
||||
} else if(tagsrc.match(/[?]node$/)) {
|
||||
AjaxIM.init({
|
||||
pollServer: imfolder + 'ajaxim.php',
|
||||
theme: imfolder + 'themes/default',
|
||||
flashStorage: jsfolder + 'jStore.Flash.html'
|
||||
}, {
|
||||
poll: 'http://' + nodehost + '/poll',
|
||||
send: 'http://' + nodehost + '/send',
|
||||
status: 'http://' + nodehost + '/status',
|
||||
resume: 'http://' + nodehost + '/resume'
|
||||
});
|
||||
} else if(tagsrc.match(/[?]guest$/)) {
|
||||
AjaxIM.init({
|
||||
pollServer: imfolder + 'ajaxim.php',
|
||||
theme: imfolder + 'themes/default',
|
||||
flashStorage: jsfolder + 'jStore.Flash.html'
|
||||
}, {
|
||||
poll: 'http://' + nodehost + '/poll',
|
||||
send: 'http://' + nodehost + '/send',
|
||||
status: 'http://' + nodehost + '/status',
|
||||
resume: 'http://' + nodehost + '/resume'
|
||||
});
|
||||
AjaxIM.client.login();
|
||||
}
|
||||
|
||||
AjaxIM.loaded();
|
||||
};
|
||||
})();
|
||||
@@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Flash External Object</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<script type="text/javascript">
|
||||
/**
|
||||
* This function captures the flash_ready event. We need to relay this
|
||||
* back to the parent so it knows flash is ready.
|
||||
*/
|
||||
function flash_ready(){
|
||||
parent.flash_ready();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="1" height="1" id="jStoreFlash"><param name="allowScriptAccess" value="always" /><param name="movie" value="jStore.swf" /><param name="quality" value="high" /><param name="bgcolor" value="#ffcc00" /><embed src="jStore.swf" quality="high" bgcolor="#ffcc00" width="1" height="1" name="jStoreFlash" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /></object>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* jQuery JSONP Core Plugin 1.1.0 (2009-10-06)
|
||||
*
|
||||
* http://code.google.com/p/jquery-jsonp/
|
||||
*
|
||||
* Copyright (c) 2009 Julian Aubourg
|
||||
*
|
||||
* This document is licensed as free software under the terms of the
|
||||
* MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
(function($){
|
||||
|
||||
// ###################### UTILITIES ##
|
||||
// Test a value is neither undefined nor null
|
||||
var defined = function(v) {
|
||||
return v!==undefined && v!==null;
|
||||
},
|
||||
// Call if defined
|
||||
callIfDefined = function(method,object,parameters) {
|
||||
defined(method) && method.apply(object,parameters);
|
||||
},
|
||||
// Let the current thread running
|
||||
later = function(functor) {
|
||||
setTimeout(functor,0);
|
||||
},
|
||||
// String constants (for better minification)
|
||||
empty="",
|
||||
amp="&",
|
||||
qMark="?",
|
||||
success = "success",
|
||||
error = "error",
|
||||
|
||||
// Head element (for faster use)
|
||||
head = $("head"),
|
||||
// Page cache
|
||||
pageCache = {},
|
||||
|
||||
// ###################### DEFAULT OPTIONS ##
|
||||
xOptionsDefaults = {
|
||||
//beforeSend: undefined,
|
||||
//cache: false,
|
||||
callback: "C",
|
||||
//callbackParameter: undefined,
|
||||
//complete: undefined,
|
||||
//data: ""
|
||||
//dataFilter: undefined,
|
||||
//error: undefined,
|
||||
//pageCache: false,
|
||||
//success: undefined,
|
||||
//timeout: 0,
|
||||
url: location.href
|
||||
},
|
||||
|
||||
// ###################### MAIN FUNCTION ##
|
||||
jsonp = function(xOptions) {
|
||||
|
||||
// Build data with default
|
||||
xOptions = $.extend({},xOptionsDefaults,xOptions);
|
||||
|
||||
// References to beforeSend (for better minification)
|
||||
var beforeSendCallback = xOptions.beforeSend,
|
||||
|
||||
// Abort/done flag
|
||||
done = 0;
|
||||
|
||||
// Put a temporary abort
|
||||
xOptions.abort = function() { done = 1; };
|
||||
|
||||
// Call beforeSend if provided (early abort if false returned)
|
||||
if (defined(beforeSendCallback) && (beforeSendCallback(xOptions,xOptions)===false || done))
|
||||
return xOptions;
|
||||
|
||||
// References to xOptions members (for better minification)
|
||||
var successCallback = xOptions.success,
|
||||
completeCallback = xOptions.complete,
|
||||
errorCallback = xOptions.error,
|
||||
dataFilter = xOptions.dataFilter,
|
||||
callbackParameter = xOptions.callbackParameter,
|
||||
successCallbackName = xOptions.callback,
|
||||
cacheFlag = xOptions.cache,
|
||||
pageCacheFlag = xOptions.pageCache,
|
||||
url = xOptions.url,
|
||||
data = xOptions.data,
|
||||
timeout = xOptions.timeout,
|
||||
|
||||
// Misc variables
|
||||
splitUrl,splitData,i,j;
|
||||
|
||||
// Control entries
|
||||
url = defined(url)?url:empty;
|
||||
data = defined(data)?((typeof data)=="string"?data:$.param(data)):empty;
|
||||
|
||||
// Add callback parameter if provided as option
|
||||
defined(callbackParameter)
|
||||
&& (data += (data==empty?empty:amp)+escape(callbackParameter)+"=?");
|
||||
|
||||
// Add anticache parameter if needed
|
||||
!cacheFlag && !pageCacheFlag
|
||||
&& (data += (data==empty?empty:amp)+"_"+(new Date()).getTime()+"=");
|
||||
|
||||
// Search for ? in url
|
||||
splitUrl = url.split(qMark);
|
||||
// Also in parameters if provided
|
||||
// (and merge arrays)
|
||||
if (data!=empty) {
|
||||
splitData = data.split(qMark);
|
||||
j = splitUrl.length-1;
|
||||
j && (splitUrl[j] += amp + splitData.shift());
|
||||
splitUrl = splitUrl.concat(splitData);
|
||||
}
|
||||
// If more than 2 ? replace the last one by the callback
|
||||
i = splitUrl.length-2;
|
||||
i && (splitUrl[i] += successCallbackName + splitUrl.pop());
|
||||
|
||||
// Build the final url
|
||||
var finalUrl = splitUrl.join(qMark),
|
||||
|
||||
// Utility function
|
||||
notifySuccess = function(json) {
|
||||
// Apply the data filter if provided
|
||||
defined(dataFilter) && (json = dataFilter.apply(xOptions,[json]));
|
||||
// Call success then complete
|
||||
callIfDefined(successCallback,xOptions,[json,success]);
|
||||
callIfDefined(completeCallback,xOptions,[xOptions,success]);
|
||||
},
|
||||
notifyError = function(type) {
|
||||
// Call error then complete
|
||||
callIfDefined(errorCallback,xOptions,[xOptions,type]);
|
||||
callIfDefined(completeCallback,xOptions,[xOptions,type]);
|
||||
},
|
||||
|
||||
// Get from pageCache
|
||||
pageCached = pageCache[finalUrl];
|
||||
|
||||
// Check page cache
|
||||
if (pageCacheFlag && defined(pageCached)) {
|
||||
later(function() {
|
||||
// If an error was cached
|
||||
defined(pageCached.s)
|
||||
? notifySuccess(pageCached.s)
|
||||
: notifyError(error);
|
||||
});
|
||||
return xOptions;
|
||||
}
|
||||
|
||||
|
||||
// Create & write to the iframe (sends the request)
|
||||
// We let the hand to current code to avoid
|
||||
// pre-emptive callbacks
|
||||
|
||||
// We also install the timeout here to avoid
|
||||
// timeout before the code has been dumped to the frame
|
||||
// (in case of insanely short timeout values)
|
||||
later(function() {
|
||||
|
||||
// If it has been aborted, do nothing
|
||||
if (done) return;
|
||||
|
||||
// Create an iframe & add it to the document
|
||||
var frame = $("<iframe />").appendTo(head),
|
||||
|
||||
// Get the iframe's window and document objects
|
||||
tmp = frame[0],
|
||||
window = tmp.contentWindow || tmp.contentDocument,
|
||||
document = window.document,
|
||||
|
||||
// Declaration of cleanup function
|
||||
cleanUp,
|
||||
|
||||
// Declaration of timer for timeout (so we can clear it eventually)
|
||||
timeoutTimer,
|
||||
|
||||
// Error function
|
||||
errorFunction = function (_,type) {
|
||||
// If pure error (not timeout), cache if needed
|
||||
pageCacheFlag && !defined(type) && (pageCache[finalUrl] = empty);
|
||||
// Cleanup
|
||||
cleanUp();
|
||||
// Call error then complete
|
||||
notifyError(defined(type)?type:error);
|
||||
},
|
||||
|
||||
// Iframe variable cleaning function
|
||||
removeVariable = function(varName) {
|
||||
window[varName] = undefined;
|
||||
try { delete window[varName]; } catch(_) {}
|
||||
},
|
||||
|
||||
// Error callback name
|
||||
errorCallbackName = successCallbackName=="E"?"X":"E";
|
||||
|
||||
// Control if we actually retrieved the document
|
||||
if(!defined(document)) {
|
||||
document = window;
|
||||
window = document.getParentNode();
|
||||
}
|
||||
|
||||
// We have to open the document before
|
||||
// declaring variables in the iframe's window
|
||||
// Don't ask me why, I have no clue
|
||||
document.open();
|
||||
|
||||
// Install callbacks
|
||||
window[successCallbackName] = function(json) {
|
||||
// Set as treated
|
||||
done = 1;
|
||||
// Pagecache is needed
|
||||
pageCacheFlag && (pageCache[finalUrl] = {s: json});
|
||||
// Give hand back to frame
|
||||
// To finish gracefully
|
||||
later(function(){
|
||||
// Cleanup
|
||||
cleanUp();
|
||||
// Call success then complete
|
||||
notifySuccess(json);
|
||||
});
|
||||
};
|
||||
|
||||
window[errorCallbackName] = function(state) {
|
||||
// If not treated, mark
|
||||
// then give hand back to iframe
|
||||
// for it to finish gracefully
|
||||
(!state || state=="complete") && !done++ && later(errorFunction);
|
||||
};
|
||||
|
||||
// Clean up function (declaration)
|
||||
xOptions.abort = cleanUp = function() {
|
||||
// Clear the timeout (is it exists)
|
||||
clearTimeout(timeoutTimer);
|
||||
// Open the iframes document & clean
|
||||
document.open();
|
||||
removeVariable(errorCallbackName);
|
||||
removeVariable(successCallbackName);
|
||||
document.write(empty);
|
||||
document.close();
|
||||
frame.remove();
|
||||
};
|
||||
|
||||
// Write to the document
|
||||
document.write([
|
||||
'<html><head><script src="',
|
||||
finalUrl,'" onload="',
|
||||
errorCallbackName,'()" onreadystatechange="',
|
||||
errorCallbackName,'(this.readyState)"></script></head><body onload="',
|
||||
errorCallbackName,'()"></body></html>'
|
||||
].join(empty)
|
||||
);
|
||||
|
||||
// Close (makes some browsers happier)
|
||||
document.close();
|
||||
|
||||
// If a timeout is needed, install it
|
||||
timeout>0 && (timeoutTimer = setTimeout(function(){
|
||||
!done && errorFunction(empty,"timeout");
|
||||
},timeout));
|
||||
});
|
||||
|
||||
return xOptions;
|
||||
}
|
||||
|
||||
// ###################### SETUP FUNCTION ##
|
||||
jsonp.setup = function(xOptions) {
|
||||
$.extend(xOptionsDefaults,xOptions);
|
||||
};
|
||||
|
||||
// ###################### INSTALL in jQuery ##
|
||||
$.jsonp = jsonp;
|
||||
|
||||
})(jQuery);
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* jStore - Persistent Client-Side Storage
|
||||
*
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*
|
||||
* Dual licensed under:
|
||||
* MIT: http://www.opensource.org/licenses/mit-license.php
|
||||
* GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
|
||||
*/
|
||||
/*
|
||||
* jQuery JSON Plugin
|
||||
* version: 1.0 (2008-04-17)
|
||||
*
|
||||
* This document is licensed as free software under the terms of the
|
||||
* MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Brantley Harris technically wrote this plugin, but it is based somewhat
|
||||
* on the JSON.org website's http://www.json.org/json2.js, which proclaims:
|
||||
* "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
|
||||
* I uphold. I really just cleaned it up.
|
||||
*
|
||||
* It is also based heavily on MochiKit's serializeJSON, which is
|
||||
* copywrited 2005 by Bob Ippolito.
|
||||
*/
|
||||
(function($){function toIntegersAtLease(n){return n<10?"0"+n:n}Date.prototype.toJSON=function(date){return this.getUTCFullYear()+"-"+toIntegersAtLease(this.getUTCMonth())+"-"+toIntegersAtLease(this.getUTCDate())};var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g;var meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};$.quoteString=function(string){if(escapeable.test(string)){return'"'+string.replace(escapeable,function(a){var c=meta[a];if(typeof c==="string"){return c}c=a.charCodeAt();return"\\u00"+Math.floor(c/16).toString(16)+(c%16).toString(16)})+'"'}return'"'+string+'"'};$.toJSON=function(o,compact){var type=typeof(o);if(type=="undefined"){return"undefined"}else{if(type=="number"||type=="boolean"){return o+""}else{if(o===null){return"null"}}}if(type=="string"){return $.quoteString(o)}if(type=="object"&&typeof o.toJSON=="function"){return o.toJSON(compact)}if(type!="function"&&typeof(o.length)=="number"){var ret=[];for(var i=0;i<o.length;i++){ret.push($.toJSON(o[i],compact))}if(compact){return"["+ret.join(",")+"]"}else{return"["+ret.join(", ")+"]"}}if(type=="function"){throw new TypeError("Unable to convert object of type 'function' to json.")}var ret=[];for(var k in o){var name;type=typeof(k);if(type=="number"){name='"'+k+'"'}else{if(type=="string"){name=$.quoteString(k)}else{continue}}var val=$.toJSON(o[k],compact);if(typeof(val)!="string"){continue}if(compact){ret.push(name+":"+val)}else{ret.push(name+": "+val)}}return"{"+ret.join(", ")+"}"};$.compactJSON=function(o){return $.toJSON(o,true)};$.evalJSON=function(src){return eval("("+src+")")};$.secureEvalJSON=function(src){var filtered=src;filtered=filtered.replace(/\\["\\\/bfnrtu]/g,"@");filtered=filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]");filtered=filtered.replace(/(?:^|:|,)(?:\s*\[)+/g,"");if(/^[\],:{}\s]*$/.test(filtered)){return eval("("+src+")")}else{throw new SyntaxError("Error parsing JSON, source is not valid.")}}})(jQuery);(function(){var a=false,b=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){};Class.extend=function(g){var f=this.prototype;a=true;var e=new this();a=false;for(var d in g){e[d]=typeof g[d]=="function"&&typeof f[d]=="function"&&b.test(g[d])?(function(h,i){return function(){var k=this._super;this._super=f[h];var j=i.apply(this,arguments);this._super=k;return j}})(d,g[d]):g[d]}function c(){if(!a&&this.init){this.init.apply(this,arguments)}}c.prototype=e;c.constructor=c;c.extend=arguments.callee;return c}})();
|
||||
/*
|
||||
* jStore Delegate Framework
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function(a){this.jStoreDelegate=Class.extend({init:function(b){this.parent=b;this.callbacks={}},bind:function(b,c){if(!a.isFunction(c)){return this}if(!this.callbacks[b]){this.callbacks[b]=[]}this.callbacks[b].push(c);return this},trigger:function(){var d=this.parent,c=[].slice.call(arguments),e=c.shift(),b=this.callbacks[e];if(!b){return false}a.each(b,function(){this.apply(d,c)});return this}})})(jQuery);(function(b){var a;try{a=new RegExp('^("(\\\\.|[^"\\\\\\n\\r])*?"|[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t])+?$')}catch(c){a=/^(true|false|null|\[.*\]|\{.*\}|".*"|\d+|\d+\.\d+)$/}b.jStore={};b.extend(b.jStore,{EngineOrder:[],Availability:{},Engines:{},Instances:{},CurrentEngine:null,defaults:{project:null,engine:null,autoload:true,flash:"jStore.Flash.html"},isReady:false,isFlashReady:false,delegate:new jStoreDelegate(b.jStore).bind("jStore-ready",function(d){b.jStore.isReady=true;if(b.jStore.defaults.autoload){d.connect()}}).bind("flash-ready",function(){b.jStore.isFlashReady=true}),ready:function(d){if(b.jStore.isReady){d.apply(b.jStore,[b.jStore.CurrentEngine])}else{b.jStore.delegate.bind("jStore-ready",d)}},fail:function(d){b.jStore.delegate.bind("jStore-failure",d)},flashReady:function(d){if(b.jStore.isFlashReady){d.apply(b.jStore,[b.jStore.CurrentEngine])}else{b.jStore.delegate.bind("flash-ready",d)}},use:function(g,i,f){i=i||b.jStore.defaults.project||location.hostname.replace(/\./g,"-")||"unknown";var h=b.jStore.Engines[g.toLowerCase()]||null,d=(f?f+".":"")+i+"."+g;if(!h){throw"JSTORE_ENGINE_UNDEFINED"}h=new h(i,d);if(b.jStore.Instances[d]){throw"JSTORE_JRI_CONFLICT"}if(h.isAvailable()){b.jStore.Instances[d]=h;if(!b.jStore.CurrentEngine){b.jStore.CurrentEngine=h}b.jStore.delegate.trigger("jStore-ready",h)}else{if(!h.autoload){throw"JSTORE_ENGINE_UNAVILABLE"}else{h.included(function(){if(this.isAvailable()){b.jStore.Instances[d]=this;if(!b.jStore.CurrentEngine){b.jStore.CurrentEngine=this}b.jStore.delegate.trigger("jStore-ready",this)}else{b.jStore.delegate.trigger("jStore-failure",this)}}).include()}}},setCurrentEngine:function(d){if(!b.jStore.Instances.length){return b.jStore.FindEngine()}if(!d&&b.jStore.Instances.length>=1){b.jStore.delegate.trigger("jStore-ready",b.jStore.Instances[0]);return b.jStore.CurrentEngine=b.jStore.Instances[0]}if(d&&b.jStore.Instances[d]){b.jStore.delegate.trigger("jStore-ready",b.jStore.Instances[d]);return b.jStore.CurrentEngine=b.jStore.Instances[d]}throw"JSTORE_JRI_NO_MATCH"},FindEngine:function(){b.each(b.jStore.EngineOrder,function(d){if(b.jStore.Availability[this]()){b.jStore.use(this,b.jStore.defaults.project,"default");return false}})},load:function(){if(b.jStore.defaults.engine){return b.jStore.use(b.jStore.defaults.engine,b.jStore.defaults.project,"default")}try{b.jStore.FindEngine()}catch(d){}},safeStore:function(d){switch(typeof d){case"object":case"function":return b.compactJSON(d);case"number":case"boolean":case"string":case"xml":return d;case"undefined":default:return""}},safeResurrect:function(d){return a.test(d)?b.evalJSON(d):d},store:function(d,e){if(!b.jStore.CurrentEngine){return false}if(!e){return b.jStore.CurrentEngine.get(d)}return b.jStore.CurrentEngine.set(d,e)},remove:function(d){if(!b.jStore.CurrentEngine){return false}return b.jStore.CurrentEngine.rem(d)},get:function(d){return b.jStore.store(d)},set:function(d,e){return b.jStore.store(d,e)}});b.extend(b.fn,{store:function(e,f){if(!b.jStore.CurrentEngine){return this}var d=b.jStore.store(e,f);return !f?d:this},removeStore:function(d){b.jStore.remove(d);return this},getStore:function(d){return b.jStore.store(d)},setStore:function(d,e){b.jStore.store(d,e);return this}})})(jQuery);(function(a){this.StorageEngine=Class.extend({init:function(c,b){this.project=c;this.jri=b;this.data={};this.limit=-1;this.includes=[];this.delegate=new jStoreDelegate(this).bind("engine-ready",function(){this.isReady=true}).bind("engine-included",function(){this.hasIncluded=true});this.autoload=false;this.isReady=false;this.hasIncluded=false},include:function(){var b=this,d=this.includes.length,c=0;a.each(this.includes,function(){a.ajax({type:"get",url:this,dataType:"script",cache:true,success:function(){c++;if(c==d){b.delegate.trigger("engine-included")}}})})},isAvailable:function(){return false},interruptAccess:function(){if(!this.isReady){throw"JSTORE_ENGINE_NOT_READY"}},ready:function(b){if(this.isReady){b.apply(this)}else{this.delegate.bind("engine-ready",b)}return this},included:function(b){if(this.hasIncluded){b.apply(this)}else{this.delegate.bind("engine-included",b)}return this},get:function(b){this.interruptAccess();return this.data[b]||null},set:function(b,c){this.interruptAccess();this.data[b]=c;return c},rem:function(b){this.interruptAccess();var c=this.data[b];this.data[b]=null;return c}})})(jQuery);
|
||||
/*
|
||||
* jStore DOM Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function(c){var b=c.jStore.Availability.session=function(){return !!window.sessionStorage},a=c.jStore.Availability.local=function(){return !!(window.localStorage||window.globalStorage)};this.jStoreDom=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="DOM";this.limit=5*1024*1024},connect:function(){this.delegate.trigger("engine-ready")},get:function(e){this.interruptAccess();var d=this.db.getItem(e);return c.jStore.safeResurrect((d&&d.value?d.value:d))},set:function(d,e){this.interruptAccess();this.db.setItem(d,c.jStore.safeStore(e));return e},rem:function(e){this.interruptAccess();var d=this.get(e);this.db.removeItem(e);return d}});this.jStoreLocal=jStoreDom.extend({connect:function(){this.db=!window.globalStorage?window.localStorage:window.globalStorage[location.hostname];this._super()},isAvailable:a});this.jStoreSession=jStoreDom.extend({connect:function(){this.db=sessionStorage;this._super()},isAvailable:b});c.jStore.Engines.local=jStoreLocal;c.jStore.Engines.session=jStoreSession;c.jStore.EngineOrder[1]="local"})(jQuery);
|
||||
/*
|
||||
* jStore Flash Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
* jStore.swf Copyright (c) 2008 Daniel Bulli (http://www.nuff-respec.com)
|
||||
*/
|
||||
(function(b){var a=b.jStore.Availability.flash=function(){return !!(b.jStore.hasFlash("8.0.0"))};this.jStoreFlash=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="Flash";var c=this;b.jStore.flashReady(function(){c.flashReady()})},connect:function(){var c="jstore-flash-embed-"+this.project;b(document.body).append('<iframe style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" id="jStoreFlashFrame" src="'+b.jStore.defaults.flash+'"></iframe>')},flashReady:function(f){var c=b("#jStoreFlashFrame")[0];if(c.Document&&b.isFunction(c.Document.jStoreFlash.f_get_cookie)){this.db=c.Document.jStoreFlash}else{if(c.contentWindow&&c.contentWindow.document){var d=c.contentWindow.document;if(b.isFunction(b("object",b(d))[0].f_get_cookie)){this.db=b("object",b(d))[0]}else{if(b.isFunction(b("embed",b(d))[0].f_get_cookie)){this.db=b("embed",b(d))[0]}}}}if(this.db){this.delegate.trigger("engine-ready")}},isAvailable:a,get:function(d){this.interruptAccess();var c=this.db.f_get_cookie(d);return c=="null"?null:b.jStore.safeResurrect(c)},set:function(c,d){this.interruptAccess();this.db.f_set_cookie(c,b.jStore.safeStore(d));return d},rem:function(c){this.interruptAccess();var d=this.get(c);this.db.f_delete_cookie(c);return d}});b.jStore.Engines.flash=jStoreFlash;b.jStore.EngineOrder[2]="flash";b.jStore.hasFlash=function(c){var e=b.jStore.flashVersion().match(/\d+/g),f=c.match(/\d+/g);for(var d=0;d<3;d++){e[d]=parseInt(e[d]||0);f[d]=parseInt(f[d]||0);if(e[d]<f[d]){return false}if(e[d]>f[d]){return true}}return true};b.jStore.flashVersion=function(){try{try{var c=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");try{c.AllowScriptAccess="always"}catch(d){return"6,0,0"}}catch(d){}return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}catch(d){try{if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){return(navigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}}catch(d){}}return"0,0,0"}})(jQuery);function flash_ready(){$.jStore.delegate.trigger("flash-ready")}
|
||||
/*
|
||||
* jStore Google Gears Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function(b){var a=b.jStore.Availability.gears=function(){return !!(window.google&&window.google.gears)};this.jStoreGears=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="Google Gears";this.includes.push("http://code.google.com/apis/gears/gears_init.js");this.autoload=true},connect:function(){var c=this.db=google.gears.factory.create("beta.database");c.open("jstore-"+this.project);c.execute("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)");this.updateCache()},updateCache:function(){var c=this.db.execute("SELECT k,v FROM jstore");while(c.isValidRow()){this.data[c.field(0)]=b.jStore.safeResurrect(c.field(1));c.next()}c.close();this.delegate.trigger("engine-ready")},isAvailable:a,set:function(d,e){this.interruptAccess();var c=this.db;c.execute("BEGIN");c.execute("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[d,b.jStore.safeStore(e)]);c.execute("COMMIT");return this._super(d,e)},rem:function(d){this.interruptAccess();var c=this.db;c.execute("BEGIN");c.execute("DELETE FROM jstore WHERE k = ?",[d]);c.execute("COMMIT");return this._super(d)}});b.jStore.Engines.gears=jStoreGears;b.jStore.EngineOrder[3]="gears"})(jQuery);
|
||||
/*
|
||||
* jStore HTML5 Specification Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function(b){var a=b.jStore.Availability.html5=function(){return !!window.openDatabase};this.jStoreHtml5=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="HTML5";this.limit=1024*200},connect:function(){var c=this.db=openDatabase("jstore-"+this.project,"1.0",this.project,this.limit);if(!c){throw"JSTORE_ENGINE_HTML5_NODB"}c.transaction(function(d){d.executeSql("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)")});this.updateCache()},updateCache:function(){var c=this;this.db.transaction(function(d){d.executeSql("SELECT k,v FROM jstore",[],function(f,e){var h=e.rows,g=0,j;for(;g<h.length;++g){j=h.item(g);c.data[j.k]=b.jStore.safeResurrect(j.v)}c.delegate.trigger("engine-ready")})})},isAvailable:a,set:function(c,d){this.interruptAccess();this.db.transaction(function(e){e.executeSql("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[c,b.jStore.safeStore(d)])});return this._super(c,d)},rem:function(c){this.interruptAccess();this.db.transaction(function(d){d.executeSql("DELETE FROM jstore WHERE k = ?",[c])});return this._super(c)}});b.jStore.Engines.html5=jStoreHtml5;b.jStore.EngineOrder[0]="html5"})(jQuery);
|
||||
/**
|
||||
* jStore IE Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function(b){var a=b.jStore.Availability.ie=function(){return !!window.ActiveXObject};this.jStoreIE=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="IE";this.limit=64*1024},connect:function(){this.db=b('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-'+this.project+'"></div>').appendTo(document.body).get(0);this.delegate.trigger("engine-ready")},isAvailable:a,get:function(c){this.interruptAccess();this.db.load(this.project);return b.jStore.safeResurrect(this.db.getAttribute(c))},set:function(c,d){this.interruptAccess();this.db.setAttribute(c,b.jStore.safeStore(d));this.db.save(this.project);return d},rem:function(c){this.interruptAccess();var d=this.get(c);this.db.removeAttribute(c);this.db.save(this.project);return d}});b.jStore.Engines.ie=jStoreIE;b.jStore.EngineOrder[4]="ie"})(jQuery);
|
||||
@@ -1,963 +0,0 @@
|
||||
/*!
|
||||
* jStore - Persistent Client-Side Storage
|
||||
*
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*
|
||||
* Dual licensed under:
|
||||
* MIT: http://www.opensource.org/licenses/mit-license.php
|
||||
* GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
|
||||
*/
|
||||
/*!
|
||||
* jQuery JSON Plugin
|
||||
* version: 1.0 (2008-04-17)
|
||||
*
|
||||
* This document is licensed as free software under the terms of the
|
||||
* MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Brantley Harris technically wrote this plugin, but it is based somewhat
|
||||
* on the JSON.org website's http://www.json.org/json2.js, which proclaims:
|
||||
* "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
|
||||
* I uphold. I really just cleaned it up.
|
||||
*
|
||||
* It is also based heavily on MochiKit's serializeJSON, which is
|
||||
* copywrited 2005 by Bob Ippolito.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
function toIntegersAtLease(n)
|
||||
// Format integers to have at least two digits.
|
||||
{
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
Date.prototype.toJSON = function(date)
|
||||
// Yes, it polutes the Date namespace, but we'll allow it here, as
|
||||
// it's damned usefull.
|
||||
{
|
||||
return this.getUTCFullYear() + '-' +
|
||||
toIntegersAtLease(this.getUTCMonth()) + '-' +
|
||||
toIntegersAtLease(this.getUTCDate());
|
||||
};
|
||||
|
||||
var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
|
||||
var meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
|
||||
$.quoteString = function(string)
|
||||
// Places quotes around a string, inteligently.
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe escape
|
||||
// sequences.
|
||||
{
|
||||
if (escapeable.test(string))
|
||||
{
|
||||
return '"' + string.replace(escapeable, function (a)
|
||||
{
|
||||
var c = meta[a];
|
||||
if (typeof c === 'string') {
|
||||
return c;
|
||||
}
|
||||
c = a.charCodeAt();
|
||||
return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
|
||||
}) + '"';
|
||||
}
|
||||
return '"' + string + '"';
|
||||
};
|
||||
|
||||
$.toJSON = function(o, compact)
|
||||
{
|
||||
var type = typeof(o);
|
||||
|
||||
if (type == "undefined")
|
||||
return "undefined";
|
||||
else if (type == "number" || type == "boolean")
|
||||
return o + "";
|
||||
else if (o === null)
|
||||
return "null";
|
||||
|
||||
// Is it a string?
|
||||
if (type == "string")
|
||||
{
|
||||
return $.quoteString(o);
|
||||
}
|
||||
|
||||
// Does it have a .toJSON function?
|
||||
if (type == "object" && typeof o.toJSON == "function")
|
||||
return o.toJSON(compact);
|
||||
|
||||
// Is it an array?
|
||||
if (type != "function" && typeof(o.length) == "number")
|
||||
{
|
||||
var ret = [];
|
||||
for (var i = 0; i < o.length; i++) {
|
||||
ret.push( $.toJSON(o[i], compact) );
|
||||
}
|
||||
if (compact)
|
||||
return "[" + ret.join(",") + "]";
|
||||
else
|
||||
return "[" + ret.join(", ") + "]";
|
||||
}
|
||||
|
||||
// If it's a function, we have to warn somebody!
|
||||
if (type == "function") {
|
||||
throw new TypeError("Unable to convert object of type 'function' to json.");
|
||||
}
|
||||
|
||||
// It's probably an object, then.
|
||||
var ret = [];
|
||||
for (var k in o) {
|
||||
var name;
|
||||
type = typeof(k);
|
||||
|
||||
if (type == "number")
|
||||
name = '"' + k + '"';
|
||||
else if (type == "string")
|
||||
name = $.quoteString(k);
|
||||
else
|
||||
continue; //skip non-string or number keys
|
||||
|
||||
var val = $.toJSON(o[k], compact);
|
||||
if (typeof(val) != "string") {
|
||||
// skip non-serializable values
|
||||
continue;
|
||||
}
|
||||
|
||||
if (compact)
|
||||
ret.push(name + ":" + val);
|
||||
else
|
||||
ret.push(name + ": " + val);
|
||||
}
|
||||
return "{" + ret.join(", ") + "}";
|
||||
};
|
||||
|
||||
$.compactJSON = function(o)
|
||||
{
|
||||
return $.toJSON(o, true);
|
||||
};
|
||||
|
||||
$.evalJSON = function(src)
|
||||
// Evals JSON that we know to be safe.
|
||||
{
|
||||
try {
|
||||
return eval("(" + src + ")");
|
||||
} catch(e) {
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
$.secureEvalJSON = function(src)
|
||||
// Evals JSON in a way that is *more* secure.
|
||||
{
|
||||
var filtered = src;
|
||||
filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
|
||||
filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
|
||||
filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
|
||||
|
||||
if (/^[\],:{}\s]*$/.test(filtered))
|
||||
return eval("(" + src + ")");
|
||||
else
|
||||
throw new SyntaxError("Error parsing JSON, source is not valid.");
|
||||
};
|
||||
})(jQuery);
|
||||
/**
|
||||
* Javascript Class Framework
|
||||
*
|
||||
* Copyright (c) 2008 John Resig (http://ejohn.org/blog/simple-javascript-inheritance/)
|
||||
* Inspired by base2 and Prototype
|
||||
*/
|
||||
(function(){
|
||||
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
||||
|
||||
// The base Class implementation (does nothing)
|
||||
this.Class = function(){};
|
||||
|
||||
// Create a new Class that inherits from this class
|
||||
Class.extend = function(prop) {
|
||||
var _super = this.prototype;
|
||||
|
||||
// Instantiate a base class (but only create the instance,
|
||||
// don't run the init constructor)
|
||||
initializing = true;
|
||||
var prototype = new this();
|
||||
initializing = false;
|
||||
|
||||
// Copy the properties over onto the new prototype
|
||||
for (var name in prop) {
|
||||
// Check if we're overwriting an existing function
|
||||
prototype[name] = typeof prop[name] == "function" &&
|
||||
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
|
||||
(function(name, fn){
|
||||
return function() {
|
||||
var tmp = this._super;
|
||||
|
||||
// Add a new ._super() method that is the same method
|
||||
// but on the super-class
|
||||
this._super = _super[name];
|
||||
|
||||
// The method only need to be bound temporarily, so we
|
||||
// remove it when we're done executing
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
|
||||
return ret;
|
||||
};
|
||||
})(name, prop[name]) :
|
||||
prop[name];
|
||||
}
|
||||
|
||||
// The dummy class constructor
|
||||
function Class() {
|
||||
// All construction is actually done in the init method
|
||||
if ( !initializing && this.init )
|
||||
this.init.apply(this, arguments);
|
||||
}
|
||||
|
||||
// Populate our constructed prototype object
|
||||
Class.prototype = prototype;
|
||||
|
||||
// Enforce the constructor to be what we expect
|
||||
Class.constructor = Class;
|
||||
|
||||
// And make this class extendable
|
||||
Class.extend = arguments.callee;
|
||||
|
||||
return Class;
|
||||
};
|
||||
})();
|
||||
/*!
|
||||
* jStore Delegate Framework
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function($){
|
||||
|
||||
this.jStoreDelegate = Class.extend({
|
||||
init: function(parent){
|
||||
// The Object this delgate operates for
|
||||
this.parent = parent;
|
||||
// Container for callbacks to dispatch.
|
||||
// eventType => [ callback, callback, ... ]
|
||||
this.callbacks = {};
|
||||
},
|
||||
bind: function(event, callback){
|
||||
if ( !$.isFunction(callback) ) return this;
|
||||
if ( !this.callbacks[ event ] ) this.callbacks[ event ] = [];
|
||||
|
||||
this.callbacks[ event ].push(callback);
|
||||
|
||||
return this;
|
||||
},
|
||||
trigger: function(){
|
||||
var parent = this.parent,
|
||||
args = [].slice.call(arguments),
|
||||
event = args.shift(),
|
||||
handlers = this.callbacks[ event ];
|
||||
|
||||
if ( !handlers ) return false;
|
||||
|
||||
$.each(handlers, function(){ this.apply(parent, args) });
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
/**
|
||||
* jStore-jQuery Interface
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function($){
|
||||
|
||||
var rxJson;
|
||||
|
||||
try {
|
||||
rxJson = new RegExp('^("(\\\\.|[^"\\\\\\n\\r])*?"|[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t])+?$')
|
||||
} catch (e) {
|
||||
rxJson = /^(true|false|null|\[.*\]|\{.*\}|".*"|\d+|\d+\.\d+)$/
|
||||
}
|
||||
|
||||
// Setup the jStore namespace in jQuery for options storage
|
||||
$.jStore = {};
|
||||
|
||||
// Seed the object
|
||||
$.extend($.jStore, {
|
||||
EngineOrder: [],
|
||||
// Engines should put their availability tests within jStore.Availability
|
||||
Availability: {},
|
||||
// Defined engines should enter themselves into the jStore.Engines
|
||||
Engines: {},
|
||||
// Instanciated engines should exist within jStore.Instances
|
||||
Instances: {},
|
||||
// The current engine to use for storage
|
||||
CurrentEngine: null,
|
||||
// Provide global settings for overwriting
|
||||
defaults: {
|
||||
project: null,
|
||||
engine: null,
|
||||
autoload: true,
|
||||
flash: 'jStore.Flash.html'
|
||||
},
|
||||
// Boolean for ready state handling
|
||||
isReady: false,
|
||||
// Boolean for flash ready state handling
|
||||
isFlashReady: false,
|
||||
// An event delegate
|
||||
delegate: new jStoreDelegate($.jStore)
|
||||
.bind('jStore-ready', function(engine){
|
||||
$.jStore.isReady = true;
|
||||
if ($.jStore.defaults.autoload) engine.connect();
|
||||
})
|
||||
.bind('flash-ready', function(){
|
||||
$.jStore.isFlashReady = true;
|
||||
}),
|
||||
// Enable ready callback for jStore
|
||||
ready: function(callback){
|
||||
if ($.jStore.isReady) callback.apply($.jStore, [$.jStore.CurrentEngine]);
|
||||
else $.jStore.delegate.bind('jStore-ready', callback);
|
||||
},
|
||||
// Enable failure callback registration for jStore
|
||||
fail: function(callback){
|
||||
$.jStore.delegate.bind('jStore-failure', callback);
|
||||
},
|
||||
// Enable ready callback for Flash
|
||||
flashReady: function(callback){
|
||||
if ($.jStore.isFlashReady) callback.apply($.jStore, [$.jStore.CurrentEngine]);
|
||||
else $.jStore.delegate.bind('flash-ready', callback);
|
||||
},
|
||||
// Enable and test an engine
|
||||
use: function(engine, project, identifier){
|
||||
project = project || $.jStore.defaults.project || location.hostname.replace(/\./g, '-') || 'unknown';
|
||||
|
||||
var e = $.jStore.Engines[engine.toLowerCase()] || null,
|
||||
name = (identifier ? identifier + '.' : '') + project + '.' + engine;
|
||||
|
||||
if ( !e ) throw 'JSTORE_ENGINE_UNDEFINED';
|
||||
|
||||
// Instanciate the engine
|
||||
e = new e(project, name);
|
||||
|
||||
// Prevent against naming conflicts
|
||||
if ($.jStore.Instances[name]) throw 'JSTORE_JRI_CONFLICT';
|
||||
|
||||
// Test the engine
|
||||
if (e.isAvailable()){
|
||||
$.jStore.Instances[name] = e; // The Easy Way
|
||||
if (!$.jStore.CurrentEngine){
|
||||
$.jStore.CurrentEngine = e;
|
||||
}
|
||||
$.jStore.delegate.trigger('jStore-ready', e);
|
||||
} else {
|
||||
if (!e.autoload) // Not available
|
||||
throw 'JSTORE_ENGINE_UNAVILABLE';
|
||||
else { // The hard way
|
||||
e.included(function(){
|
||||
if (this.isAvailable()) { // Worked out
|
||||
$.jStore.Instances[name] = this;
|
||||
// If there is no current engine, use this one
|
||||
if (!$.jStore.CurrentEngine){
|
||||
$.jStore.CurrentEngine = this;
|
||||
}
|
||||
$.jStore.delegate.trigger('jStore-ready', this);
|
||||
}
|
||||
else $.jStore.delegate.trigger('jStore-failure', this);
|
||||
}).include();
|
||||
}
|
||||
}
|
||||
},
|
||||
// Set the current storage engine
|
||||
setCurrentEngine: function(name){
|
||||
if (!$.jStore.Instances.length ) // If no instances exist, attempt to load one
|
||||
return $.jStore.FindEngine();
|
||||
|
||||
if (!name && $.jStore.Instances.length >= 1) { // If no name is specified, use the first engine
|
||||
$.jStore.delegate.trigger('jStore-ready', $.jStore.Instances[0]);
|
||||
return $.jStore.CurrentEngine = $.jStore.Instances[0];
|
||||
}
|
||||
|
||||
if (name && $.jStore.Instances[name]) { // If a name is specified and exists, use it
|
||||
$.jStore.delegate.trigger('jStore-ready', $.jStore.Instances[name]);
|
||||
return $.jStore.CurrentEngine = $.jStore.Instances[name];
|
||||
}
|
||||
|
||||
throw 'JSTORE_JRI_NO_MATCH';
|
||||
},
|
||||
// Test all possible engines for straightforward useability
|
||||
FindEngine: function(){
|
||||
$.each($.jStore.EngineOrder, function(k){
|
||||
if ($.jStore.Availability[this]()){ // Find the first, easiest option and use it.
|
||||
$.jStore.use(this, $.jStore.defaults.project, 'default');
|
||||
return false;
|
||||
}
|
||||
})
|
||||
},
|
||||
// Provide a way for users to call for auto-loading
|
||||
load: function(){
|
||||
if ($.jStore.defaults.engine)
|
||||
return $.jStore.use($.jStore.defaults.engine, $.jStore.defaults.project, 'default');
|
||||
|
||||
// Attempt to find a valid engine, and catch any exceptions if we can't
|
||||
try {
|
||||
$.jStore.FindEngine();
|
||||
} catch (e) {}
|
||||
},
|
||||
// Parse a value as JSON before its stored.
|
||||
safeStore: function(value){
|
||||
switch (typeof value){
|
||||
case 'object': case 'function': return $.compactJSON(value);
|
||||
case 'number': case 'boolean': case 'string': case 'xml': return value;
|
||||
case 'undefined': default: return '';
|
||||
}
|
||||
},
|
||||
// Restores JSON'd values before returning
|
||||
safeResurrect: function(value){
|
||||
return rxJson.test(value) ? $.evalJSON(value) : value;
|
||||
},
|
||||
// Provide a simple interface for storing/getting values
|
||||
store: function(key, value){
|
||||
if (!$.jStore.CurrentEngine) return false;
|
||||
|
||||
if ( !value ) // Executing a get command
|
||||
return $.jStore.CurrentEngine.get(key);
|
||||
// Executing a set command
|
||||
return $.jStore.CurrentEngine.set(key, value);
|
||||
},
|
||||
// Provide a simple interface for removing values
|
||||
remove: function(key){
|
||||
if (!$.jStore.CurrentEngine) return false;
|
||||
|
||||
return $.jStore.CurrentEngine.rem(key);
|
||||
},
|
||||
// Alias access for reading
|
||||
get: function(key){
|
||||
return $.jStore.store(key);
|
||||
},
|
||||
// Alias access for setting
|
||||
set: function(key, value){
|
||||
return $.jStore.store(key, value);
|
||||
}
|
||||
})
|
||||
|
||||
// Extend the jQuery funcitonal object
|
||||
$.extend($.fn, {
|
||||
// Provide a chainable interface for storing values/getting a value at the end of a chain
|
||||
store: function(key, value){
|
||||
if (!$.jStore.CurrentEngine) return this;
|
||||
|
||||
var result = $.jStore.store(key, value);
|
||||
|
||||
return !value ? result : this;
|
||||
},
|
||||
// Provide a chainable interface for removing values
|
||||
removeStore: function(key){
|
||||
$.jStore.remove(key);
|
||||
|
||||
return this;
|
||||
},
|
||||
// Alias access for reading at the end of a chain.
|
||||
getStore: function(key){
|
||||
return $.jStore.store(key);
|
||||
},
|
||||
// Alias access for setting on a chanin.
|
||||
setStore: function(key, value){
|
||||
$.jStore.store(key, value);
|
||||
return this;
|
||||
}
|
||||
})
|
||||
|
||||
})(jQuery);
|
||||
/**
|
||||
* jStore Engine Core
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function($){
|
||||
|
||||
this.StorageEngine = Class.extend({
|
||||
init: function(project, name){
|
||||
// Configure the project name
|
||||
this.project = project;
|
||||
// The JRI name given by the manager
|
||||
this.jri = name;
|
||||
// Cache the data so we can work synchronously
|
||||
this.data = {};
|
||||
// The maximum limit of the storage engine
|
||||
this.limit = -1;
|
||||
// Third party script includes
|
||||
this.includes = [];
|
||||
// Create an event delegate for users to subscribe to event triggers
|
||||
this.delegate = new jStoreDelegate(this)
|
||||
.bind('engine-ready', function(){
|
||||
this.isReady = true;
|
||||
})
|
||||
.bind('engine-included', function(){
|
||||
this.hasIncluded = true;
|
||||
});
|
||||
// If enabled, the manager will check availability, then run include(), then check again
|
||||
this.autoload = false; // This should be changed by the engines, if they have required includes
|
||||
// When set, we're ready to transact data
|
||||
this.isReady = false;
|
||||
// When the includer is finished, it will set this to true
|
||||
this.hasIncluded = false;
|
||||
},
|
||||
// Performs all necessary script includes
|
||||
include: function(){
|
||||
var self = this,
|
||||
total = this.includes.length,
|
||||
count = 0;
|
||||
|
||||
$.each(this.includes, function(){
|
||||
$.ajax({type: 'get', url: this, dataType: 'script', cache: true,
|
||||
success: function(){
|
||||
count++;
|
||||
if (count == total) self.delegate.trigger('engine-included');
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
// This should be overloaded with an actual functionality presence check
|
||||
isAvailable: function(){
|
||||
return false;
|
||||
},
|
||||
// All get/set/rem functions across the engines should add this to the
|
||||
// first line of those functions to prevent accessing the engine while unstable.
|
||||
interruptAccess: function(){
|
||||
if (!this.isReady) throw 'JSTORE_ENGINE_NOT_READY';
|
||||
},
|
||||
/** Event Subscription Shortcuts **/
|
||||
ready: function(callback){
|
||||
if (this.isReady) callback.apply(this);
|
||||
else this.delegate.bind('engine-ready', callback);
|
||||
return this;
|
||||
},
|
||||
included: function(callback){
|
||||
if (this.hasIncluded) callback.apply(this);
|
||||
else this.delegate.bind('engine-included', callback);
|
||||
return this;
|
||||
},
|
||||
/** Cache Data Access **/
|
||||
get: function(key){
|
||||
this.interruptAccess();
|
||||
return this.data[key] || null;
|
||||
},
|
||||
set: function(key, value){
|
||||
this.interruptAccess();
|
||||
this.data[key] = value;
|
||||
return value;
|
||||
},
|
||||
rem: function(key){
|
||||
this.interruptAccess();
|
||||
var beforeDelete = this.data[key];
|
||||
this.data[key] = null;
|
||||
return beforeDelete;
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
/*!
|
||||
* jStore DOM Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function($){
|
||||
|
||||
// Set up a static test function for this instance
|
||||
var sessionAvailability = $.jStore.Availability.session = function(){
|
||||
return !!window.sessionStorage;
|
||||
},
|
||||
localAvailability = $.jStore.Availability.local = function(){
|
||||
return !!(window.localStorage || window.globalStorage);
|
||||
};
|
||||
|
||||
this.jStoreDom = StorageEngine.extend({
|
||||
init: function(project, name){
|
||||
// Call the parental init object
|
||||
this._super(project, name);
|
||||
|
||||
// The type of storage engine
|
||||
this.type = 'DOM';
|
||||
|
||||
// Set the Database limit
|
||||
this.limit = 5 * 1024 * 1024;
|
||||
},
|
||||
connect: function(){
|
||||
// Fire our delegate to indicate we're ready for data transactions
|
||||
this.delegate.trigger('engine-ready');
|
||||
},
|
||||
get: function(key){
|
||||
this.interruptAccess();
|
||||
var out = this.db.getItem(key);
|
||||
// Gecko's getItem returns {value: 'the value'}, WebKit returns 'the value'
|
||||
return $.jStore.safeResurrect( (out && out.value ? out.value : out) );
|
||||
},
|
||||
set: function(key, value){
|
||||
this.interruptAccess();
|
||||
this.db.setItem(key,$.jStore.safeStore(value));
|
||||
return value;
|
||||
},
|
||||
rem: function(key){
|
||||
this.interruptAccess();
|
||||
var out = this.get(key);
|
||||
this.db.removeItem(key);
|
||||
return out
|
||||
}
|
||||
})
|
||||
|
||||
this.jStoreLocal = jStoreDom.extend({
|
||||
connect: function(){
|
||||
// Gecko uses a non-standard globalStorage[ www.example.com ] DOM access object for persistant storage.
|
||||
this.db = !window.globalStorage ? window.localStorage : window.globalStorage[location.hostname];
|
||||
this._super();
|
||||
},
|
||||
isAvailable: localAvailability
|
||||
})
|
||||
|
||||
this.jStoreSession = jStoreDom.extend({
|
||||
connect: function(){
|
||||
this.db = sessionStorage;
|
||||
this._super();
|
||||
},
|
||||
isAvailable: sessionAvailability
|
||||
})
|
||||
|
||||
$.jStore.Engines.local = jStoreLocal;
|
||||
$.jStore.Engines.session = jStoreSession;
|
||||
|
||||
// Store the ordering preference
|
||||
$.jStore.EngineOrder[ 1 ] = 'local';
|
||||
|
||||
})(jQuery);
|
||||
/*!
|
||||
* jStore Flash Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
* jStore.swf Copyright (c) 2008 Daniel Bulli (http://www.nuff-respec.com)
|
||||
*/
|
||||
(function($){
|
||||
|
||||
// Set up a static test function for this instance
|
||||
var avilability = $.jStore.Availability.flash = function(){
|
||||
return !!($.jStore.hasFlash('8.0.0'));
|
||||
}
|
||||
|
||||
this.jStoreFlash = StorageEngine.extend({
|
||||
init: function(project, name){
|
||||
// Call the parental init object
|
||||
this._super(project, name);
|
||||
|
||||
// The type of storage engine
|
||||
this.type = 'Flash';
|
||||
|
||||
// Bind our flashReady function to the jStore Delegate
|
||||
var self = this;
|
||||
$.jStore.flashReady(function(){ self.flashReady() });
|
||||
},
|
||||
connect: function(){
|
||||
var name = 'jstore-flash-embed-' + this.project;
|
||||
|
||||
// To make Flash Storage work on IE, we have to load up an iFrame
|
||||
// which contains an HTML page that embeds the object using an
|
||||
// object tag wrapping an embed tag. Of course, this is unnecessary for
|
||||
// all browsers except for IE, which, to my knowledge, is the only browser
|
||||
// in existance where you need to complicate your code to fix bugs. Goddamnit. :(
|
||||
$(document.body)
|
||||
.append('<iframe style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" ' +
|
||||
'id="jStoreFlashFrame" src="' +$.jStore.defaults.flash + '"></iframe>');
|
||||
},
|
||||
flashReady: function(e){
|
||||
var iFrame = $('#jStoreFlashFrame')[0];
|
||||
|
||||
// IE
|
||||
if (iFrame.Document && $.isFunction(iFrame.Document['jStoreFlash'].f_get_cookie)) this.db = iFrame.Document['jStoreFlash'];
|
||||
// Safari && Firefox
|
||||
else if (iFrame.contentWindow && iFrame.contentWindow.document){
|
||||
var doc = iFrame.contentWindow.document;
|
||||
// Safari
|
||||
if ($.isFunction($('object', $(doc))[0].f_get_cookie)) this.db = $('object', $(doc))[0];
|
||||
// Firefox
|
||||
else if ($.isFunction($('embed', $(doc))[0].f_get_cookie)) this.db = $('embed', $(doc))[0];
|
||||
}
|
||||
|
||||
// We're ready to process data
|
||||
if (this.db) this.delegate.trigger('engine-ready');
|
||||
},
|
||||
isAvailable: avilability,
|
||||
get: function(key){
|
||||
this.interruptAccess();
|
||||
var out = this.db.f_get_cookie(key);
|
||||
return out == 'null' ? null : $.jStore.safeResurrect(out);
|
||||
},
|
||||
set: function(key, value){
|
||||
this.interruptAccess();
|
||||
this.db.f_set_cookie(key, $.jStore.safeStore(value));
|
||||
return value;
|
||||
},
|
||||
rem: function(key){
|
||||
this.interruptAccess();
|
||||
var beforeDelete = this.get(key);
|
||||
this.db.f_delete_cookie(key);
|
||||
return beforeDelete;
|
||||
}
|
||||
})
|
||||
|
||||
$.jStore.Engines.flash = jStoreFlash;
|
||||
|
||||
// Store the ordering preference
|
||||
$.jStore.EngineOrder[ 2 ] = 'flash';
|
||||
|
||||
/**
|
||||
* Flash Detection functions copied from the jQuery Flash Plugin
|
||||
* Copyright (c) 2006 Luke Lutman (http://jquery.lukelutman.com/plugins/flash)
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.opensource.org/licenses/gpl-license.php
|
||||
*/
|
||||
$.jStore.hasFlash = function(version){
|
||||
var pv = $.jStore.flashVersion().match(/\d+/g),
|
||||
rv = version.match(/\d+/g);
|
||||
|
||||
for(var i = 0; i < 3; i++) {
|
||||
pv[i] = parseInt(pv[i] || 0);
|
||||
rv[i] = parseInt(rv[i] || 0);
|
||||
// player is less than required
|
||||
if(pv[i] < rv[i]) return false;
|
||||
// player is greater than required
|
||||
if(pv[i] > rv[i]) return true;
|
||||
}
|
||||
// major version, minor version and revision match exactly
|
||||
return true;
|
||||
}
|
||||
|
||||
$.jStore.flashVersion = function(){
|
||||
// ie
|
||||
try {
|
||||
try {
|
||||
// avoid fp6 minor version lookup issues
|
||||
// see: http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
|
||||
var axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
|
||||
try { axo.AllowScriptAccess = 'always'; }
|
||||
catch(e) { return '6,0,0'; }
|
||||
} catch(e) {
|
||||
return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
|
||||
}
|
||||
// other browsers
|
||||
} catch(e) {
|
||||
try {
|
||||
if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
|
||||
return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
return '0,0,0';
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
|
||||
// Callback fired when ExternalInterface is established
|
||||
function flash_ready(){
|
||||
$.jStore.delegate.trigger('flash-ready');
|
||||
}
|
||||
/*!
|
||||
* jStore Google Gears Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function($){
|
||||
|
||||
// Set up a static test function for this instance
|
||||
var avilability = $.jStore.Availability.gears = function(){
|
||||
return !!(window.google && window.google.gears)
|
||||
}
|
||||
|
||||
this.jStoreGears = StorageEngine.extend({
|
||||
init: function(project, name){
|
||||
// Call the parental init object
|
||||
this._super(project, name);
|
||||
|
||||
// The type of storage engine
|
||||
this.type = 'Google Gears';
|
||||
|
||||
// Add required third-party scripts
|
||||
this.includes.push('http://code.google.com/apis/gears/gears_init.js');
|
||||
|
||||
// Allow Autoloading on fail
|
||||
this.autoload = true;
|
||||
},
|
||||
connect: function(){
|
||||
// Create our database connection
|
||||
var db = this.db = google.gears.factory.create('beta.database');
|
||||
db.open( 'jstore-' + this.project );
|
||||
db.execute( 'CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)' );
|
||||
|
||||
// Cache the data from the table
|
||||
this.updateCache();
|
||||
},
|
||||
updateCache: function(){
|
||||
// Read the database into our cache object
|
||||
var result = this.db.execute( 'SELECT k,v FROM jstore' );
|
||||
while (result.isValidRow()){
|
||||
this.data[result.field(0)] = $.jStore.safeResurrect( result.field(1) );
|
||||
result.next();
|
||||
} result.close();
|
||||
|
||||
// Fire our delegate to indicate we're ready for data transactions
|
||||
this.delegate.trigger('engine-ready');
|
||||
},
|
||||
isAvailable: avilability,
|
||||
set: function(key, value){
|
||||
this.interruptAccess();
|
||||
// Update the database
|
||||
var db = this.db;
|
||||
db.execute( 'BEGIN' );
|
||||
db.execute( 'INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)', [key,$.jStore.safeStore(value)] );
|
||||
db.execute( 'COMMIT' );
|
||||
return this._super(key, value);
|
||||
},
|
||||
rem: function(key){
|
||||
this.interruptAccess();
|
||||
// Update the database
|
||||
var db = this.db;
|
||||
db.execute( 'BEGIN' );
|
||||
db.execute( 'DELETE FROM jstore WHERE k = ?', [key] );
|
||||
db.execute( 'COMMIT' );
|
||||
return this._super(key);
|
||||
}
|
||||
})
|
||||
|
||||
$.jStore.Engines.gears = jStoreGears;
|
||||
|
||||
// Store the ordering preference
|
||||
$.jStore.EngineOrder[ 3 ] = 'gears';
|
||||
|
||||
})(jQuery);
|
||||
/*!
|
||||
* jStore HTML5 Specification Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function($){
|
||||
|
||||
// Set up a static test function for this instance
|
||||
var avilability = $.jStore.Availability.html5 = function(){
|
||||
return !!window.openDatabase
|
||||
}
|
||||
|
||||
this.jStoreHtml5 = StorageEngine.extend({
|
||||
init: function(project, name){
|
||||
// Call the parental init object
|
||||
this._super(project, name);
|
||||
|
||||
// The type of storage engine
|
||||
this.type = 'HTML5';
|
||||
|
||||
// Set the Database limit
|
||||
this.limit = 1024 * 200;
|
||||
},
|
||||
connect: function(){
|
||||
// Create our database connection
|
||||
var db = this.db = openDatabase('jstore-' + this.project, '1.0', this.project, this.limit);
|
||||
if (!db) throw 'JSTORE_ENGINE_HTML5_NODB';
|
||||
db.transaction(function(db){
|
||||
db.executeSql( 'CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)' );
|
||||
});
|
||||
|
||||
// Cache the data from the table
|
||||
this.updateCache();
|
||||
},
|
||||
updateCache: function(){
|
||||
var self = this;
|
||||
// Read the database into our cache object
|
||||
this.db.transaction(function(db){
|
||||
db.executeSql( 'SELECT k,v FROM jstore', [], function(db, result){
|
||||
var rows = result.rows, i = 0, row;
|
||||
for (; i < rows.length; ++i){
|
||||
row = rows.item(i);
|
||||
self.data[row.k] = $.jStore.safeResurrect( row.v );
|
||||
}
|
||||
|
||||
// Fire our delegate to indicate we're ready for data transactions
|
||||
self.delegate.trigger('engine-ready');
|
||||
});
|
||||
});
|
||||
},
|
||||
isAvailable: avilability,
|
||||
set: function(key, value){
|
||||
this.interruptAccess();
|
||||
// Update the database
|
||||
this.db.transaction(function(db){
|
||||
db.executeSql( 'INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)', [key,$.jStore.safeStore(value)]);
|
||||
});
|
||||
return this._super(key, value);
|
||||
},
|
||||
rem: function(key){
|
||||
this.interruptAccess();
|
||||
// Update the database
|
||||
this.db.transaction(function(db){
|
||||
db.executeSql( 'DELETE FROM jstore WHERE k = ?', [key] )
|
||||
})
|
||||
return this._super(key);
|
||||
}
|
||||
})
|
||||
|
||||
$.jStore.Engines.html5 = jStoreHtml5;
|
||||
|
||||
// Store the ordering preference
|
||||
$.jStore.EngineOrder[ 0 ] = 'html5';
|
||||
|
||||
})(jQuery);
|
||||
/*!*
|
||||
* jStore IE Storage Engine
|
||||
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
|
||||
*/
|
||||
(function($){
|
||||
|
||||
// Set up a static test function for this instance
|
||||
var avilability = $.jStore.Availability.ie = function(){
|
||||
return !!window.ActiveXObject;
|
||||
}
|
||||
|
||||
this.jStoreIE = StorageEngine.extend({
|
||||
init: function(project, name){
|
||||
// Call the parental init object
|
||||
this._super(project, name);
|
||||
|
||||
// The type of storage engine
|
||||
this.type = 'IE';
|
||||
|
||||
// Allow Autoloading on fail
|
||||
this.limit = 64 * 1024;
|
||||
},
|
||||
connect: function(){
|
||||
// Create a hidden div to store attributes in
|
||||
this.db = $('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-' + this.project + '"></div>')
|
||||
.appendTo(document.body).get(0);
|
||||
// Fire our delegate to indicate we're ready for data transactions
|
||||
this.delegate.trigger('engine-ready');
|
||||
},
|
||||
isAvailable: avilability,
|
||||
get: function(key){
|
||||
this.interruptAccess();
|
||||
this.db.load(this.project);
|
||||
return $.jStore.safeResurrect( this.db.getAttribute(key) );
|
||||
},
|
||||
set: function(key, value){
|
||||
this.interruptAccess();
|
||||
this.db.setAttribute(key, $.jStore.safeStore(value));
|
||||
this.db.save(this.project);
|
||||
return value;
|
||||
},
|
||||
rem: function(key){
|
||||
this.interruptAccess();
|
||||
var beforeDelete = this.get(key);
|
||||
this.db.removeAttribute(key);
|
||||
this.db.save(this.project);
|
||||
return beforeDelete;
|
||||
}
|
||||
})
|
||||
|
||||
$.jStore.Engines.ie = jStoreIE;
|
||||
|
||||
// Store the ordering preference
|
||||
$.jStore.EngineOrder[ 4 ] = 'ie';
|
||||
|
||||
})(jQuery);
|
||||
@@ -1,230 +0,0 @@
|
||||
|
||||
/**
|
||||
* jQuery MD5 hash algorithm function
|
||||
*
|
||||
* <code>
|
||||
* Calculate the md5 hash of a String
|
||||
* String $.md5 ( String str )
|
||||
* </code>
|
||||
*
|
||||
* Calculates the MD5 hash of str using the » RSA Data Security, Inc. MD5 Message-Digest Algorithm, and returns that hash.
|
||||
* MD5 (Message-Digest algorithm 5) is a widely-used cryptographic hash function with a 128-bit hash value. MD5 has been employed in a wide variety of security applications, and is also commonly used to check the integrity of data. The generated hash is also non-reversable. Data cannot be retrieved from the message digest, the digest uniquely identifies the data.
|
||||
* MD5 was developed by Professor Ronald L. Rivest in 1994. Its 128 bit (16 byte) message digest makes it a faster implementation than SHA-1.
|
||||
* This script is used to process a variable length message into a fixed-length output of 128 bits using the MD5 algorithm. It is fully compatible with UTF-8 encoding. It is very useful when u want to transfer encrypted passwords over the internet. If you plan using UTF-8 encoding in your project don't forget to set the page encoding to UTF-8 (Content-Type meta tag).
|
||||
* This function orginally get from the WebToolkit and rewrite for using as the jQuery plugin.
|
||||
*
|
||||
* Example
|
||||
* Code
|
||||
* <code>
|
||||
* $.md5("I'm Persian.");
|
||||
* </code>
|
||||
* Result
|
||||
* <code>
|
||||
* "b8c901d0f02223f9761016cfff9d68df"
|
||||
* </code>
|
||||
*
|
||||
* @alias Muhammad Hussein Fattahizadeh < muhammad [AT] semnanweb [DOT] com >
|
||||
* @link http://www.semnanweb.com/jquery-plugin/md5.html
|
||||
* @see http://www.webtoolkit.info/
|
||||
* @license http://www.gnu.org/licenses/gpl.html [GNU General Public License]
|
||||
* @param {jQuery} {md5:function(string))
|
||||
* @return string
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
var rotateLeft = function(lValue, iShiftBits) {
|
||||
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
|
||||
}
|
||||
|
||||
var addUnsigned = function(lX, lY) {
|
||||
var lX4, lY4, lX8, lY8, lResult;
|
||||
lX8 = (lX & 0x80000000);
|
||||
lY8 = (lY & 0x80000000);
|
||||
lX4 = (lX & 0x40000000);
|
||||
lY4 = (lY & 0x40000000);
|
||||
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
|
||||
if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
|
||||
if (lX4 | lY4) {
|
||||
if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
|
||||
else return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
|
||||
} else {
|
||||
return (lResult ^ lX8 ^ lY8);
|
||||
}
|
||||
}
|
||||
|
||||
var F = function(x, y, z) {
|
||||
return (x & y) | ((~ x) & z);
|
||||
}
|
||||
|
||||
var G = function(x, y, z) {
|
||||
return (x & z) | (y & (~ z));
|
||||
}
|
||||
|
||||
var H = function(x, y, z) {
|
||||
return (x ^ y ^ z);
|
||||
}
|
||||
|
||||
var I = function(x, y, z) {
|
||||
return (y ^ (x | (~ z)));
|
||||
}
|
||||
|
||||
var FF = function(a, b, c, d, x, s, ac) {
|
||||
a = addUnsigned(a, addUnsigned(addUnsigned(F(b, c, d), x), ac));
|
||||
return addUnsigned(rotateLeft(a, s), b);
|
||||
};
|
||||
|
||||
var GG = function(a, b, c, d, x, s, ac) {
|
||||
a = addUnsigned(a, addUnsigned(addUnsigned(G(b, c, d), x), ac));
|
||||
return addUnsigned(rotateLeft(a, s), b);
|
||||
};
|
||||
|
||||
var HH = function(a, b, c, d, x, s, ac) {
|
||||
a = addUnsigned(a, addUnsigned(addUnsigned(H(b, c, d), x), ac));
|
||||
return addUnsigned(rotateLeft(a, s), b);
|
||||
};
|
||||
|
||||
var II = function(a, b, c, d, x, s, ac) {
|
||||
a = addUnsigned(a, addUnsigned(addUnsigned(I(b, c, d), x), ac));
|
||||
return addUnsigned(rotateLeft(a, s), b);
|
||||
};
|
||||
|
||||
var convertToWordArray = function(string) {
|
||||
var lWordCount;
|
||||
var lMessageLength = string.length;
|
||||
var lNumberOfWordsTempOne = lMessageLength + 8;
|
||||
var lNumberOfWordsTempTwo = (lNumberOfWordsTempOne - (lNumberOfWordsTempOne % 64)) / 64;
|
||||
var lNumberOfWords = (lNumberOfWordsTempTwo + 1) * 16;
|
||||
var lWordArray = Array(lNumberOfWords - 1);
|
||||
var lBytePosition = 0;
|
||||
var lByteCount = 0;
|
||||
while (lByteCount < lMessageLength) {
|
||||
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
|
||||
lBytePosition = (lByteCount % 4) * 8;
|
||||
lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
|
||||
lByteCount++;
|
||||
}
|
||||
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
|
||||
lBytePosition = (lByteCount % 4) * 8;
|
||||
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
|
||||
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
|
||||
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
|
||||
return lWordArray;
|
||||
};
|
||||
|
||||
var wordToHex = function(lValue) {
|
||||
var WordToHexValue = "", WordToHexValueTemp = "", lByte, lCount;
|
||||
for (lCount = 0; lCount <= 3; lCount++) {
|
||||
lByte = (lValue >>> (lCount * 8)) & 255;
|
||||
WordToHexValueTemp = "0" + lByte.toString(16);
|
||||
WordToHexValue = WordToHexValue + WordToHexValueTemp.substr(WordToHexValueTemp.length - 2, 2);
|
||||
}
|
||||
return WordToHexValue;
|
||||
};
|
||||
|
||||
var uTF8Encode = function(string) {
|
||||
string = string.replace(/\x0d\x0a/g, "\x0a");
|
||||
var output = "";
|
||||
for (var n = 0; n < string.length; n++) {
|
||||
var c = string.charCodeAt(n);
|
||||
if (c < 128) {
|
||||
output += String.fromCharCode(c);
|
||||
} else if ((c > 127) && (c < 2048)) {
|
||||
output += String.fromCharCode((c >> 6) | 192);
|
||||
output += String.fromCharCode((c & 63) | 128);
|
||||
} else {
|
||||
output += String.fromCharCode((c >> 12) | 224);
|
||||
output += String.fromCharCode(((c >> 6) & 63) | 128);
|
||||
output += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
$.extend({
|
||||
md5: function(string) {
|
||||
var x = Array();
|
||||
var k, AA, BB, CC, DD, a, b, c, d;
|
||||
var S11=7, S12=12, S13=17, S14=22;
|
||||
var S21=5, S22=9 , S23=14, S24=20;
|
||||
var S31=4, S32=11, S33=16, S34=23;
|
||||
var S41=6, S42=10, S43=15, S44=21;
|
||||
string = uTF8Encode(string);
|
||||
x = convertToWordArray(string);
|
||||
a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
|
||||
for (k = 0; k < x.length; k += 16) {
|
||||
AA = a; BB = b; CC = c; DD = d;
|
||||
a = FF(a, b, c, d, x[k+0], S11, 0xD76AA478);
|
||||
d = FF(d, a, b, c, x[k+1], S12, 0xE8C7B756);
|
||||
c = FF(c, d, a, b, x[k+2], S13, 0x242070DB);
|
||||
b = FF(b, c, d, a, x[k+3], S14, 0xC1BDCEEE);
|
||||
a = FF(a, b, c, d, x[k+4], S11, 0xF57C0FAF);
|
||||
d = FF(d, a, b, c, x[k+5], S12, 0x4787C62A);
|
||||
c = FF(c, d, a, b, x[k+6], S13, 0xA8304613);
|
||||
b = FF(b, c, d, a, x[k+7], S14, 0xFD469501);
|
||||
a = FF(a, b, c, d, x[k+8], S11, 0x698098D8);
|
||||
d = FF(d, a, b, c, x[k+9], S12, 0x8B44F7AF);
|
||||
c = FF(c, d, a, b, x[k+10], S13, 0xFFFF5BB1);
|
||||
b = FF(b, c, d, a, x[k+11], S14, 0x895CD7BE);
|
||||
a = FF(a, b, c, d, x[k+12], S11, 0x6B901122);
|
||||
d = FF(d, a, b, c, x[k+13], S12, 0xFD987193);
|
||||
c = FF(c, d, a, b, x[k+14], S13, 0xA679438E);
|
||||
b = FF(b, c, d, a, x[k+15], S14, 0x49B40821);
|
||||
a = GG(a, b, c, d, x[k+1], S21, 0xF61E2562);
|
||||
d = GG(d, a, b, c, x[k+6], S22, 0xC040B340);
|
||||
c = GG(c, d, a, b, x[k+11], S23, 0x265E5A51);
|
||||
b = GG(b, c, d, a, x[k+0], S24, 0xE9B6C7AA);
|
||||
a = GG(a, b, c, d, x[k+5], S21, 0xD62F105D);
|
||||
d = GG(d, a, b, c, x[k+10], S22, 0x2441453);
|
||||
c = GG(c, d, a, b, x[k+15], S23, 0xD8A1E681);
|
||||
b = GG(b, c, d, a, x[k+4], S24, 0xE7D3FBC8);
|
||||
a = GG(a, b, c, d, x[k+9], S21, 0x21E1CDE6);
|
||||
d = GG(d, a, b, c, x[k+14], S22, 0xC33707D6);
|
||||
c = GG(c, d, a, b, x[k+3], S23, 0xF4D50D87);
|
||||
b = GG(b, c, d, a, x[k+8], S24, 0x455A14ED);
|
||||
a = GG(a, b, c, d, x[k+13], S21, 0xA9E3E905);
|
||||
d = GG(d, a, b, c, x[k+2], S22, 0xFCEFA3F8);
|
||||
c = GG(c, d, a, b, x[k+7], S23, 0x676F02D9);
|
||||
b = GG(b, c, d, a, x[k+12], S24, 0x8D2A4C8A);
|
||||
a = HH(a, b, c, d, x[k+5], S31, 0xFFFA3942);
|
||||
d = HH(d, a, b, c, x[k+8], S32, 0x8771F681);
|
||||
c = HH(c, d, a, b, x[k+11], S33, 0x6D9D6122);
|
||||
b = HH(b, c, d, a, x[k+14], S34, 0xFDE5380C);
|
||||
a = HH(a, b, c, d, x[k+1], S31, 0xA4BEEA44);
|
||||
d = HH(d, a, b, c, x[k+4], S32, 0x4BDECFA9);
|
||||
c = HH(c, d, a, b, x[k+7], S33, 0xF6BB4B60);
|
||||
b = HH(b, c, d, a, x[k+10], S34, 0xBEBFBC70);
|
||||
a = HH(a, b, c, d, x[k+13], S31, 0x289B7EC6);
|
||||
d = HH(d, a, b, c, x[k+0], S32, 0xEAA127FA);
|
||||
c = HH(c, d, a, b, x[k+3], S33, 0xD4EF3085);
|
||||
b = HH(b, c, d, a, x[k+6], S34, 0x4881D05);
|
||||
a = HH(a, b, c, d, x[k+9], S31, 0xD9D4D039);
|
||||
d = HH(d, a, b, c, x[k+12], S32, 0xE6DB99E5);
|
||||
c = HH(c, d, a, b, x[k+15], S33, 0x1FA27CF8);
|
||||
b = HH(b, c, d, a, x[k+2], S34, 0xC4AC5665);
|
||||
a = II(a, b, c, d, x[k+0], S41, 0xF4292244);
|
||||
d = II(d, a, b, c, x[k+7], S42, 0x432AFF97);
|
||||
c = II(c, d, a, b, x[k+14], S43, 0xAB9423A7);
|
||||
b = II(b, c, d, a, x[k+5], S44, 0xFC93A039);
|
||||
a = II(a, b, c, d, x[k+12], S41, 0x655B59C3);
|
||||
d = II(d, a, b, c, x[k+3], S42, 0x8F0CCC92);
|
||||
c = II(c, d, a, b, x[k+10], S43, 0xFFEFF47D);
|
||||
b = II(b, c, d, a, x[k+1], S44, 0x85845DD1);
|
||||
a = II(a, b, c, d, x[k+8], S41, 0x6FA87E4F);
|
||||
d = II(d, a, b, c, x[k+15], S42, 0xFE2CE6E0);
|
||||
c = II(c, d, a, b, x[k+6], S43, 0xA3014314);
|
||||
b = II(b, c, d, a, x[k+13], S44, 0x4E0811A1);
|
||||
a = II(a, b, c, d, x[k+4], S41, 0xF7537E82);
|
||||
d = II(d, a, b, c, x[k+11], S42, 0xBD3AF235);
|
||||
c = II(c, d, a, b, x[k+2], S43, 0x2AD7D2BB);
|
||||
b = II(b, c, d, a, x[k+9], S44, 0xEB86D391);
|
||||
a = addUnsigned(a, AA);
|
||||
b = addUnsigned(b, BB);
|
||||
c = addUnsigned(c, CC);
|
||||
d = addUnsigned(d, DD);
|
||||
}
|
||||
var tempValue = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
|
||||
return tempValue.toLowerCase();
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
@@ -1,792 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Library: MySQL
|
||||
* Author: Joshua Gross
|
||||
* Version: 0.1 alpha
|
||||
* Date: January 16, 2010
|
||||
*
|
||||
* Description:
|
||||
* The default MySQL server backend.
|
||||
*
|
||||
* Requirements: MySQL 5.0+
|
||||
*/
|
||||
|
||||
class MySQL_Database extends BaseDatabase {
|
||||
private static $_instance = false;
|
||||
|
||||
public static function instance() {
|
||||
if(self::$_instance === false) {
|
||||
// create instance
|
||||
self::$_instance = new PDO('mysql:dbname=' . MYSQL_DATABASE . ';host=' . MYSQL_HOSTNAME, MYSQL_USERNAME, MYSQL_PASSWORD);
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
}
|
||||
|
||||
class MySQL_User extends BaseUser {
|
||||
function __construct($username, $password, $user_id, $ip='', $last_login=0) {
|
||||
parent::__construct($username, $password, $user_id, $ip, $last_login);
|
||||
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->user_id = $user_id;
|
||||
$this->ip = $ip;
|
||||
$this->last_login = $last_login;
|
||||
}
|
||||
|
||||
public static function authenticate($username, $password) {
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$username = preg_replace('/([%_])/', '\\\\\1', $username);
|
||||
|
||||
$auth_sql = "SELECT * FROM " . MYSQL_PREFIX . "users
|
||||
WHERE username LIKE :username LIMIT 1";
|
||||
$auth = self::$db->prepare($auth_sql);
|
||||
$auth->execute(array(':username' => $username));
|
||||
|
||||
if($auth->rowCount()) {
|
||||
$user = $auth->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
// hash given password using actual password hash, then test against real password
|
||||
$pw_hash = substr($user->password, 0, 8);
|
||||
|
||||
if($user->password == $pw_hash . md5($pw_hash . $password)) {
|
||||
$user_obj = new MySQL_User($user->username, $user->password, $user->user_id,
|
||||
$user->last_known_ip, $user->last_login);
|
||||
|
||||
return $user_obj;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function get($user) {
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
}
|
||||
|
||||
public static function find($username) {
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$username = preg_replace('/([%_])/', '\\\\\1', $username);
|
||||
|
||||
$find_sql = "SELECT * FROM " . MYSQL_PREFIX . "users
|
||||
WHERE username LIKE :username LIMIT 1";
|
||||
$find = self::$db->prepare($find_sql);
|
||||
$find->execute(array(':username' => $username));
|
||||
|
||||
$user = $find->fetch(PDO::FETCH_OBJ);
|
||||
return new User($user->username, $user->password, $user->user_id, $user->last_known_ip, $user->last_login);
|
||||
}
|
||||
|
||||
public function status($s=null, $message=null) {
|
||||
if(!$this->status) {
|
||||
$this->status = MySQL_Status::of($this->user_id);
|
||||
}
|
||||
|
||||
if($s) {
|
||||
return $this->status->is($s, $message);
|
||||
} else {
|
||||
return $this->status;
|
||||
}
|
||||
}
|
||||
|
||||
public function ip($ip=null) {
|
||||
if($ip) {
|
||||
$this->ip = $ip;
|
||||
|
||||
if($this->user_id && $this->autosave) {
|
||||
$updateip_sql = "UPDATE " . MYSQL_PREFIX . "users SET last_known_ip=:ip WHERE user_id=:id";
|
||||
$updateip = self::$db->prepare($updateip_sql);
|
||||
$updateip->execute(array(
|
||||
':ip' => $ip,
|
||||
':id' => $this->user_id
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
if($this->ip) {
|
||||
return $this->ip;
|
||||
} else if($this->user_id) {
|
||||
$getip_sql = "SELECT last_known_ip FROM " . MYSQL_PREFIX . "users WHERE user_id=:id";
|
||||
$getip = self::$db->prepare($getip_sql);
|
||||
$getip->execute(array(':id' => $this->user_id));
|
||||
|
||||
$user_obj = $getip->fetch(PDO::FETCH_OBJ);
|
||||
$this->ip = $user_obj->last_known_ip;
|
||||
return $this->ip;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function lastLogin($ll=null) {
|
||||
if($ll) {
|
||||
$this->last_login = $ll;
|
||||
|
||||
if($this->user_id && $this->autosave) {
|
||||
$updatell_sql = "UPDATE " . MYSQL_PREFIX . "users SET last_login=FROM_UNIXTIME(:ll) WHERE user_id=:id";
|
||||
$updatell = self::$db->prepare($updatell_sql);
|
||||
$updatell->execute(array(
|
||||
':ll' => $ll,
|
||||
':id' => $this->user_id
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
if($this->last_login) {
|
||||
return $this->last_login;
|
||||
} else if($this->user_id) {
|
||||
$getll_sql = "SELECT last_login FROM " . MYSQL_PREFIX . "users WHERE user_id=:id";
|
||||
$getll = self::$db->prepare($getll_sql);
|
||||
$getll->execute(array(':id' => $this->user_id));
|
||||
|
||||
$user_obj = $getll->fetch(PDO::FETCH_OBJ);
|
||||
$this->last_login = strtotime($user_obj->last_login);
|
||||
return $this->last_login;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function save($id=null) {
|
||||
}
|
||||
}
|
||||
|
||||
class MySQL_Status extends BaseStatus {
|
||||
const Offline = 0;
|
||||
const Available = 1;
|
||||
const Away = 2;
|
||||
const Invisible = 3;
|
||||
|
||||
public $s = null;
|
||||
public $message = '';
|
||||
public $user_id = 0;
|
||||
|
||||
function __construct($user_id, $s=null, $message='') {
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$this->s = $s;
|
||||
$this->message = $message;
|
||||
$this->user_id = $user_id;
|
||||
}
|
||||
|
||||
public static function of($user) {
|
||||
$status_sql = "SELECT s, message, s.user_id FROM " . MYSQL_PREFIX . "status as s ";
|
||||
if(is_int($user)) {
|
||||
$status_sql .= "WHERE user_id = :user";
|
||||
} else {
|
||||
$status_sql .= "LEFT JOIN " . MYSQL_PREFIX . "users as u ON u.user_id=s.user_id WHERE u.username = :user";
|
||||
}
|
||||
$status_sql .= " LIMIT 1";
|
||||
|
||||
$prep = self::$db->prepare($sql);
|
||||
$prep->execute(array(':user' => $user));
|
||||
$status = $prep->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
return new Status($status->s, $status->message, $status->user_id);
|
||||
}
|
||||
|
||||
public function is($s, $message='') {
|
||||
$status_sql = "INSERT INTO " . MYSQL_PREFIX . "status (user_id, message, status) VALUES(:user_id, :message, :s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
status = :s, message = :message";
|
||||
$prep = self::$db->prepare($status_sql);
|
||||
|
||||
$this->s = $s;
|
||||
$this->message = $message;
|
||||
|
||||
$changed_status = $prep->execute(array(
|
||||
':s' => $s,
|
||||
':message' => $message,
|
||||
':user_id' => $this->user_id
|
||||
));
|
||||
|
||||
if($changed_status) {
|
||||
// broadcast status update to friends
|
||||
$update_friends_sql = "INSERT INTO " . MYSQL_PREFIX . "messages (from_id, to_id, message, type)
|
||||
SELECT :user_id AS from_id, friend_id as to_id, :status as message, 's' as type FROM " . MYSQL_PREFIX . "friends as friends
|
||||
LEFT JOIN " . MYSQL_PREFIX . "status as status ON friends.friend_id = status.user_id WHERE friends.user_id = :user_id AND status.status > 0";
|
||||
$update_friends = self::$db->prepare($update_friends_sql);
|
||||
|
||||
$update_friends->execute(array(
|
||||
':user_id' => $this->user_id,
|
||||
':status' => $s . ':' . $message
|
||||
));
|
||||
}
|
||||
|
||||
return $changed_status;
|
||||
}
|
||||
}
|
||||
|
||||
class MySQL_Message extends BaseMessage {
|
||||
function __construct($from=0, $to=0, $message='', $type='m') {
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$this->id = 0;
|
||||
$this->from = $from;
|
||||
$this->to = $to;
|
||||
$this->message = $message;
|
||||
$this->type = $type;
|
||||
$this->sent = 0;
|
||||
|
||||
// Automatically flush changes if id is set?
|
||||
$this->autosave = true;
|
||||
}
|
||||
|
||||
public static function send($from=0, $to=0, $message='') {
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
if(isset($this) && $this->from && !$from) {
|
||||
$from = $this->from;
|
||||
$to = $this->to;
|
||||
$message = $this->message;
|
||||
}
|
||||
|
||||
$send_sql = "INSERT INTO " . MYSQL_PREFIX . "messages (from_id, to_id, type, message)
|
||||
VALUES(:from, :to, 'm', :message)";
|
||||
$prep_send = self::$db->prepare($send_sql);
|
||||
|
||||
return $prep_send->execute(array(
|
||||
':from' => $from,
|
||||
':to' => $to,
|
||||
':message' => $message
|
||||
));
|
||||
}
|
||||
|
||||
public static function get($id) {
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$get_sql = "SELECT * FROM " . MYSQL_PREFIX . "messages WHERE message_id=:id LIMIT 1";
|
||||
$get_message = self::$db->prepare($get_sql);
|
||||
$get_message->execute(array(':id' => intval($id)));
|
||||
|
||||
if($get_message->rowCount()) {
|
||||
$msg_obj = $get_message->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
$message = new Message($msg_obj->from, $msg_obj->to, $msg_obj->message, $msg_obj->type);
|
||||
$message->id = $msg_obj->message_id;
|
||||
|
||||
return $message;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getMany($from_or_to, $user) {
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$get_sql = "SELECT message_id, u1.username as `from`, u2.username as `to`, message, `type`, sent FROM " . MYSQL_PREFIX . "messages as m
|
||||
LEFT JOIN ajaxim_users as u1 ON m.to_id = u1.user_id
|
||||
LEFT JOIN ajaxim_users as u2 ON m.from_id = u2.user_id WHERE ";
|
||||
if(is_numeric($user)) {
|
||||
$get_sql .= ($from_or_to == 'from' ? 'from' : 'to') . "_id = :user";
|
||||
} else {
|
||||
$get_sql .= "u" . ($from_or_to == 'from' ? '2' : '1') . ".username = :user";
|
||||
}
|
||||
$get_sql .= " AND `read` = 0 ORDER BY sent ASC";
|
||||
|
||||
$get_messages = self::$db->prepare($get_sql);
|
||||
$get_messages->execute(array(':user' => $user));
|
||||
|
||||
if($get_messages->rowCount()) {
|
||||
$messages = array(); $message_ids = array();
|
||||
while($msg_obj = $get_messages->fetch(PDO::FETCH_OBJ)) {
|
||||
$messages[] = new Message($msg_obj->from, $msg_obj->to, $msg_obj->message, $msg_obj->type);
|
||||
$idx = count($messages) - 1;
|
||||
$messages[$idx]->sent = strtotime($msg_obj->sent);
|
||||
$messages[$idx]->id = $msg_obj->message_id;
|
||||
|
||||
$message_ids[] = $msg_obj->message_id;
|
||||
}
|
||||
|
||||
$mark_read_sql = "UPDATE " . MYSQL_PREFIX . "messages SET `read` = 1 WHERE message_id IN(" . join(',', $message_ids) . ")";
|
||||
$mark_read = self::$db->prepare($mark_read_sql);
|
||||
$mark_read->execute();
|
||||
|
||||
return $messages;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function sent($ts=null) {
|
||||
if($ts) {
|
||||
$this->sent = $ts;
|
||||
|
||||
if($this->id && $this->autosave) {
|
||||
$updatets_sql = "UPDATE " . MYSQL_PREFIX . "messages SET sent=FROM_UNIXTIME(:ts) WHERE message_id=:id";
|
||||
$updatets = self::$db->prepare($updatets_sql);
|
||||
|
||||
$updatets->execute(array(
|
||||
':ts' => $ts,
|
||||
':id' => $this->id
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
if($this->sent) {
|
||||
return $this->sent;
|
||||
} else if($this->id) {
|
||||
$getts_sql = "SELECT sent FROM " . MYSQL_PREFIX . "messages WHERE message_id=:id LIMIT 1";
|
||||
$getts = self::$db->prepare($getts_sql);
|
||||
$getts->execute(array(':id' => $this->id));
|
||||
|
||||
if($getts->rowCount()) {
|
||||
$msg_obj = $getts->fetch(PDO::FETCH_OBJ);
|
||||
$this->sent = strtotime($msg_obj->sent);
|
||||
return $this->sent;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function from($from=null) {
|
||||
// check if from is int, string, or User object
|
||||
if($from) {
|
||||
$this->from = $from;
|
||||
|
||||
if($this->id && $this->autosave) {
|
||||
$updatefrom_sql = "UPDATE " . MYSQL_PREFIX . "messages SET from=:from WHERE message_id=:id";
|
||||
$updatefrom = self::$db->prepare($updatefrom_sql);
|
||||
|
||||
$updatefrom->execute(array(
|
||||
':from' => $from,
|
||||
':id' => $this->id
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
if($this->from) {
|
||||
return $this->from;
|
||||
} else if($this->id) {
|
||||
$getfrom_sql = "SELECT from FROM " . MYSQL_PREFIX . "messages WHERE message_id=:id LIMIT 1";
|
||||
$getfrom = self::$db->prepare($getfrom_sql);
|
||||
$getfrom->execute(array(':id' => $this->id));
|
||||
|
||||
if($getfrom->rowCount()) {
|
||||
$msg_obj = $getfrom->fetch(PDO::FETCH_OBJ);
|
||||
$this->from = $msg_obj->from;
|
||||
return $this->from;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function to($to=null) {
|
||||
// check if to is int, string, or User object
|
||||
if($to) {
|
||||
$this->to = $to;
|
||||
|
||||
if($this->id && $this->autosave) {
|
||||
$updateto_sql = "UPDATE " . MYSQL_PREFIX . "messages SET to=:to WHERE message_id=:id";
|
||||
$updateto = self::$db->prepare($updateto_sql);
|
||||
|
||||
$updateto->execute(array(
|
||||
':to' => $to,
|
||||
':id' => $this->id
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
if($this->to) {
|
||||
return $this->to;
|
||||
} else if($this->id) {
|
||||
$getto_sql = "SELECT to FROM " . MYSQL_PREFIX . "messages WHERE message_id=:id LIMIT 1";
|
||||
$getto = self::$db->prepare($getto_sql);
|
||||
$getto->execute(array(':id' => $this->id));
|
||||
|
||||
if($getto->rowCount()) {
|
||||
$msg_obj = $getto->fetch(PDO::FETCH_OBJ);
|
||||
$this->to = $msg_obj->to;
|
||||
return $this->to;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function message($message=null) {
|
||||
if($message) {
|
||||
$this->message = $message;
|
||||
|
||||
if($this->id && $this->autosave) {
|
||||
$updatemsg_sql = "UPDATE " . MYSQL_PREFIX . "messages SET message=:message WHERE message_id=:id";
|
||||
$updatemsg = self::$db->prepare($updatemsg_sql);
|
||||
|
||||
$updatemsg->execute(array(
|
||||
':message' => $message,
|
||||
':id' => $this->id
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
if($this->message) {
|
||||
return $this->message;
|
||||
} else if($this->id) {
|
||||
$getmsg_sql = "SELECT message FROM " . MYSQL_PREFIX . "messages WHERE message_id=:id LIMIT 1";
|
||||
$getmsg = self::$db->prepare($getmsg_sql);
|
||||
$getmsg->execute(array(':id' => $this->id));
|
||||
|
||||
if($getmsg->rowCount()) {
|
||||
$msg_obj = $getmsg->fetch(PDO::FETCH_OBJ);
|
||||
$this->message = $msg_obj->message;
|
||||
return $this->message;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function save($id=null) {
|
||||
if(!$this->id && !$id)
|
||||
return false;
|
||||
else if($this->id)
|
||||
$id = $this->id;
|
||||
|
||||
if($id) {
|
||||
$savemsg_sql = "UPDATE " . MYSQL_PREFIX . "messages SET from_id=:from, to_id=:to, type=:type, message=:message, sent=:sent WHERE message_id=:id";
|
||||
$savemsg = self::$db->prepare($savemsg_sql);
|
||||
|
||||
$state = $savemsg->execute(array(
|
||||
':from' => $this->from,
|
||||
':to' => $this->to,
|
||||
':type' => $this->type,
|
||||
':message' => $this->message,
|
||||
':sent' => $this->sent,
|
||||
':id' => $this->id
|
||||
));
|
||||
|
||||
if($state) {
|
||||
return $id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$savemsg_sql = "INSERT INTO " . MYSQL_PREFIX . "messages (from_id, to_id, type, message, sent) VALUES (:from, :to, :type, :message, :sent)
|
||||
ON DUPLICATE KEY UPDATE message_id=LAST_INSERT_ID(message_id), from_id=:from, to_id=:to, type=:type, message=:message, sent=:sent";
|
||||
$savemsg = self::$db->prepare($savemsg_sql);
|
||||
|
||||
$state = $savemsg->execute(array(
|
||||
':from' => $this->from,
|
||||
':to' => $this->to,
|
||||
':type' => $this->type,
|
||||
':message' => $this->message,
|
||||
':sent' => $this->sent
|
||||
));
|
||||
|
||||
if($state) {
|
||||
$this->id = self::$db->lastInsertId();
|
||||
return $this->id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MySQL_Friend extends BaseFriend {
|
||||
static $db = null;
|
||||
|
||||
function __construct($user=null, $friend=null, $group=null) {
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$this->id = 0;
|
||||
$this->user = $user;
|
||||
$this->friend = $friend;
|
||||
$this->group = $group;
|
||||
|
||||
// Automatically flush changes if id is set?
|
||||
$this->autosave = true;
|
||||
}
|
||||
|
||||
public static function of($user=0, $offline=false) {
|
||||
if(!$user && (isset($this) && !$this->user))
|
||||
return array();
|
||||
|
||||
if(isset($this) && $this->user && !$user)
|
||||
$user = $this->user;
|
||||
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$friends_of_sql = "SELECT users.username as u, status.status as s, groups.name as g FROM " . MYSQL_PREFIX . "friends as friends
|
||||
LEFT JOIN " . MYSQL_PREFIX . "users as users ON friends.friend_id = users.user_id
|
||||
LEFT JOIN " . MYSQL_PREFIX . "status as status ON users.user_id = status.user_id
|
||||
LEFT JOIN " . MYSQL_PREFIX . "groups as groups ON friends.group_id = groups.group_id
|
||||
WHERE friends.user_id = :user" . ($offline ? "" : " AND status.status != 0");
|
||||
|
||||
$friends_of = self::$db->prepare($friends_of_sql);
|
||||
|
||||
$friends_of->execute(array('user' => $user));
|
||||
|
||||
if($friends_of->rowCount()) {
|
||||
return $friends_of->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
public static function get($id) {
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
$friend_get_sql = "SELECT users.username as u, status.status as s, groups.name as g FROM " . MYSQL_PREFIX . "friends as friends
|
||||
LEFT JOIN " . MYSQL_PREFIX . "users as users ON friends.friend_id = users.user_id
|
||||
LEFT JOIN " . MYSQL_PREFIX . "status as status ON users.user_id = status.user_id
|
||||
LEFT JOIN " . MYSQL_PREFIX . "groups as groups ON friends.group_id = groups.group_id
|
||||
WHERE friends.id = :id LIMIT 1";
|
||||
$friend_get = self::$db->prepare($friend_get_sql);
|
||||
|
||||
$friend_get->execute(array('id' => $id));
|
||||
|
||||
if($friend_get->rowCount()) {
|
||||
return $friend_get->fetch(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
return array('u' => false, 's' => 0, 'g' => false);
|
||||
}
|
||||
}
|
||||
|
||||
public static function find($friend, $user=0) {
|
||||
if(!$user && (isset($this) && !$this->user))
|
||||
return false;
|
||||
|
||||
if(!self::$db)
|
||||
self::$db = MySQL_Database::instance();
|
||||
|
||||
if($this->user && !$user)
|
||||
$user = $this->user;
|
||||
|
||||
$friend_find_sql = "SELECT users.username as u, status.status as s, groups.name as g FROM " . MYSQL_PREFIX . "friends as friends
|
||||
LEFT JOIN " . MYSQL_PREFIX . "users as users ON friends.friend_id = users.user_id
|
||||
LEFT JOIN " . MYSQL_PREFIX . "status as status ON users.user_id = status.user_id
|
||||
LEFT JOIN " . MYSQL_PREFIX . "groups as groups ON friends.group_id = groups.group_id ";
|
||||
|
||||
if(is_int($friend))
|
||||
$friend_find_sql .= "WHERE friends.friend_id = :friend";
|
||||
else
|
||||
$friend_find_sql .= "WHERE users.username = :friend";
|
||||
|
||||
$friend_find_sql .= " AND friends.user_id = :user LIMIT 1";
|
||||
|
||||
$friend_find = self::$db->prepare($friend_find_sql);
|
||||
|
||||
$friend_find->execute(array(
|
||||
'friend' => $friend,
|
||||
'user' => $user
|
||||
));
|
||||
|
||||
if($friend_find->rowCount()) {
|
||||
return $friend_find->fetch(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function group($group=null) {
|
||||
if($group) {
|
||||
$this->group = $group;
|
||||
|
||||
if($this->id && $this->autosave) {
|
||||
if(!is_int($this->group)) {
|
||||
$group_id_sql = "INSERT INTO " . MYSQL_PREFIX . "groups (name) VALUES (:group)
|
||||
ON DUPLICATE KEY UPDATE group_id=LAST_INSERT_ID(group_id)";
|
||||
$group_id = self::$db->prepare($group_id_sql);
|
||||
$group_id->execute(array(':group' => $group));
|
||||
if(!$group_id) {
|
||||
return false;
|
||||
} else {
|
||||
$group_id = self::$db->lastInsertId();
|
||||
$this->group = $group_id;
|
||||
}
|
||||
}
|
||||
|
||||
$updategroup_sql = "UPDATE " . MYSQL_PREFIX . "friends SET group_id=:group_id WHERE id=:id";
|
||||
$updategroup = self::$db->prepare($updatemsg_sql);
|
||||
|
||||
$updategroup->execute(array(
|
||||
':group_id' => $this->group,
|
||||
':id' => $this->id
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
if($this->group) {
|
||||
return $this->group;
|
||||
} else if($this->id) {
|
||||
$getgroup_sql = "SELECT group FROM " . MYSQL_PREFIX . "friends WHERE id=:id LIMIT 1";
|
||||
$getgroup = self::$db->prepare($getmsg_sql);
|
||||
$getgroup->execute(array(':id' => $this->id));
|
||||
|
||||
if($getgroup->rowCount()) {
|
||||
$group_obj = $getmsg->fetch(PDO::FETCH_OBJ);
|
||||
$this->group = $group_obj->group;
|
||||
return $this->group;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function save($id=null) {
|
||||
$as = $this->autosave;
|
||||
$this->autosave = false;
|
||||
|
||||
if($this->id)
|
||||
$id = $this->id;
|
||||
|
||||
if(!is_int($this->group)) {
|
||||
$group_id_sql = "INSERT INTO " . MYSQL_PREFIX . "groups (name) VALUES (:group)
|
||||
ON DUPLICATE KEY UPDATE group_id=LAST_INSERT_ID(group_id)";
|
||||
$group_id = self::$db->prepare($group_id_sql);
|
||||
$group_id->execute(array(':group' => $this->group));
|
||||
if(!$group_id) {
|
||||
return false;
|
||||
} else {
|
||||
$group_id = self::$db->lastInsertId();
|
||||
$this->group = $group_id;
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_int($this->user)) {
|
||||
$user = MySQL_User::find($this->user);
|
||||
if($user) {
|
||||
$this->user = $user->user_id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_int($this->friend)) {
|
||||
$friend = MySQL_User::find($this->friend);
|
||||
if($friend) {
|
||||
$this->friend = $user->user_id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if($id) {
|
||||
$savefriend_sql = "UPDATE " . MYSQL_PREFIX . "friends SET user_id=:user, friend_id=:friend, group_id=:group WHERE id=:id";
|
||||
$savefriend = self::$db->prepare($savefriend_sql);
|
||||
|
||||
$state = $savefriend->execute(array(
|
||||
':user' => $this->user,
|
||||
':friend' => $this->friend,
|
||||
':group' => $this->group
|
||||
));
|
||||
|
||||
if($state) {
|
||||
return $id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$savefriend_sql = "INSERT INTO " . MYSQL_PREFIX . "friends (user_id, friend_id, group_id) VALUES (:user, :friend, :group)
|
||||
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), user_id=:user, friend_id=:friend, group_id=:group";
|
||||
$savefriend = self::$db->prepare($savefriend_sql);
|
||||
|
||||
$state = $savefriend->execute(array(
|
||||
':user' => $this->user,
|
||||
':friend' => $this->friend,
|
||||
':group' => $this->group
|
||||
));
|
||||
|
||||
if($state) {
|
||||
$this->id = self::$db->lastInsertId();
|
||||
return $this->id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function remove() {
|
||||
}
|
||||
}
|
||||
|
||||
/* SQL:
|
||||
CREATE TABLE `ajaxim_friends` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`friend_id` int(11) NOT NULL,
|
||||
`group_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
|
||||
|
||||
CREATE TABLE `ajaxim_groups` (
|
||||
`group_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
PRIMARY KEY (`group_id`),
|
||||
UNIQUE KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
|
||||
|
||||
CREATE TABLE `ajaxim_messages` (
|
||||
`message_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`from_id` int(11) NOT NULL,
|
||||
`to_id` int(11) NOT NULL,
|
||||
`message` text NOT NULL,
|
||||
`type` varchar(2) NOT NULL,
|
||||
`sent` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`read` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`message_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
|
||||
|
||||
CREATE TABLE `ajaxim_status` (
|
||||
`status_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`status` int(11) NOT NULL,
|
||||
`message` text NOT NULL,
|
||||
PRIMARY KEY (`status_id`),
|
||||
UNIQUE KEY `user_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
|
||||
|
||||
CREATE TABLE `ajaxim_users` (
|
||||
`user_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(32) NOT NULL,
|
||||
`password` varchar(40) NOT NULL,
|
||||
`last_known_ip` int(11) NOT NULL,
|
||||
`last_login` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
|
||||
*/
|
||||
|
||||
/* End of libraries/db/MySQL.php */
|
||||
@@ -1,139 +0,0 @@
|
||||
<?php
|
||||
class BaseDatabase {
|
||||
private static $_instance = false;
|
||||
|
||||
public static function instance() {
|
||||
if(self::$_instance === false) {
|
||||
// create instance
|
||||
self::$_instance = true;
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
}
|
||||
|
||||
class BaseUser {
|
||||
static $db = null;
|
||||
|
||||
function __construct($username, $password, $user_id, $ip, $last_login) {
|
||||
}
|
||||
|
||||
public static function authenticate($username, $password) {
|
||||
}
|
||||
|
||||
public static function get($user) {
|
||||
}
|
||||
|
||||
public static function find($username) {
|
||||
}
|
||||
|
||||
public function status($s=null, $message=null) {
|
||||
}
|
||||
|
||||
public function ip($ip=null) {
|
||||
}
|
||||
|
||||
public function lastLogin($ll=null) {
|
||||
}
|
||||
|
||||
public function save($id=null) {
|
||||
}
|
||||
}
|
||||
|
||||
class BaseStatus {
|
||||
const Offline = 0;
|
||||
const Available = 1;
|
||||
const Away = 2;
|
||||
const Invisible = 3;
|
||||
|
||||
static $db = null;
|
||||
|
||||
function __construct($user_id, $s=null, $message='') {
|
||||
}
|
||||
|
||||
public static function of($user) {
|
||||
}
|
||||
|
||||
public function is($s, $message='') {
|
||||
}
|
||||
}
|
||||
|
||||
class BaseMessage {
|
||||
static $db = null;
|
||||
|
||||
function __construct($from='', $to='', $message='', $type='') {
|
||||
}
|
||||
|
||||
public static function send($from, $to, $message) {
|
||||
}
|
||||
|
||||
public static function get($id) {
|
||||
}
|
||||
|
||||
public static function getMany($from_or_to, $user) {
|
||||
}
|
||||
|
||||
public function sent($ts=null) {
|
||||
}
|
||||
|
||||
public function from($from=null) {
|
||||
}
|
||||
|
||||
public function to($to=null) {
|
||||
}
|
||||
|
||||
public function message($message=null) {
|
||||
}
|
||||
|
||||
public function save($id=null) {
|
||||
}
|
||||
}
|
||||
|
||||
class BaseFriend {
|
||||
static $db = null;
|
||||
|
||||
function __construct($user=null, $friend=null, $group=null) {
|
||||
}
|
||||
|
||||
public static function get($id) {
|
||||
}
|
||||
|
||||
public static function find($friend, $user=0) {
|
||||
}
|
||||
|
||||
public function group($group=null) {
|
||||
}
|
||||
|
||||
public function save() {
|
||||
}
|
||||
|
||||
public function remove() {
|
||||
}
|
||||
}
|
||||
|
||||
class BaseFriendGroup {
|
||||
static $db = null;
|
||||
|
||||
function __construct($user, $group) {
|
||||
}
|
||||
|
||||
public static function get($id) {
|
||||
}
|
||||
|
||||
public static function find($group) {
|
||||
}
|
||||
|
||||
public function addFriend($friend) {
|
||||
}
|
||||
|
||||
public function friends() {
|
||||
}
|
||||
|
||||
public function save() {
|
||||
}
|
||||
|
||||
public function remove() {
|
||||
}
|
||||
}
|
||||
|
||||
/* End of libraries/db/base.php */
|
||||
@@ -1,305 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Library: Default
|
||||
* Author: Joshua Gross
|
||||
* Version: 0.1 alpha
|
||||
* Date: February 12, 2010
|
||||
*
|
||||
* Description:
|
||||
* The default PHP-only server library. This library allows
|
||||
* you to use Ajax IM on a shared server, without installing
|
||||
* any extra software.
|
||||
*
|
||||
* Requirements: Database
|
||||
*/
|
||||
|
||||
// == Default Server Library ==
|
||||
//
|
||||
// This is the default PHP-only server library. This library allows you
|
||||
// to use Ajax IM on a shared server, without installing any extra software.
|
||||
class Default_IM extends IM {
|
||||
const FIXBUFFER = 1024; // Works around an output buffering issue in IE & Safari
|
||||
const FIXEOL = '<br/>'; // Works around an end-of-line issue in Safari
|
||||
|
||||
// === {{{Default_IM::}}}**{{{__construct()}}}** ===
|
||||
//
|
||||
// Initializes the IM library and retrieves the user's session, if one
|
||||
// currently exists.
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
session_start();
|
||||
if($_SESSION['username']) {
|
||||
$this->username = $_SESSION['username'];
|
||||
$this->user_id = $_SESSION['user_id'];
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{Default_IM::}}}**{{{login($username, $password)}}}** ===
|
||||
//
|
||||
// Authenticate a user against the database. If the user is valid,
|
||||
// log them in.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$username}}} is the user's login name.\\
|
||||
// * {{{$password}}} is an already-md5'd copy of the user's password.
|
||||
public function login($username, $password) {
|
||||
if($user = User::authenticate($username, $password)) {
|
||||
// user just logged in, update login time.
|
||||
$user->lastLogin(time());
|
||||
|
||||
$_SESSION['username'] = $user->username;
|
||||
$_SESSION['user_id'] = intval($user->user_id);
|
||||
|
||||
$online = Friend::of($user->user_id);
|
||||
|
||||
return array('r' => 'logged in', 's' => session_id(), 'f' => $online);
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'invalid user');
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{Default_IM::}}}**{{{logout()}}}** ===
|
||||
//
|
||||
// Signs the user out.
|
||||
public function logout() {
|
||||
session_destroy();
|
||||
$_SESSION = array();
|
||||
|
||||
return array('r' => 'logged out');
|
||||
}
|
||||
|
||||
// === {{{Default_IM::}}}**{{{send($to, $message)}}}** ===
|
||||
//
|
||||
// Send a message to another user by adding the message to the
|
||||
// database.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$to}}} is the username of the recipient.\\
|
||||
// * {{{$message}}} is the content.
|
||||
public function send($to, $message) {
|
||||
if(!$this->username)
|
||||
return array('r' => 'error', 'e' => 'no session found');
|
||||
|
||||
$message = $this->_sanitize($message);
|
||||
|
||||
$to = User::find($to);
|
||||
if(!$to)
|
||||
return array('r' => 'error', 'e' => 'no_recipient');
|
||||
|
||||
if(Message::send($this->user_id, $to->user_id, $message)) {
|
||||
return array('r' => 'sent');
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'send error');
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{Default_IM::}}}**{{{status($status, $message)}}}** ===
|
||||
//
|
||||
// Sets the status of the current user, including any associated
|
||||
// status message.
|
||||
public function status($status, $message) {
|
||||
if(!$this->username)
|
||||
return array('r' => 'error', 'e' => 'no session found');
|
||||
|
||||
$status = intval($status);
|
||||
$message = $this->_sanitize($message);
|
||||
|
||||
$statuses = array(Status::Offline, Status::Available, Status::Away, Status::Invisible);
|
||||
if(!in_array($status, $statuses))
|
||||
$status = Status::Available;
|
||||
|
||||
$user_status = new Status($this->user_id);
|
||||
if($user_status->is($s, $message)) {
|
||||
return array('r' => 'status set');
|
||||
|
||||
// now, notify all friends
|
||||
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'status error');
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{Default_IM::}}}**{{{register($username, $password)}}}** ===
|
||||
//
|
||||
// Create a new user based on the provided username and password.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$username}}} is the new user's login name.\\
|
||||
// * {{{$password}}} is the user's plaintext password.
|
||||
public function register($username, $password) {
|
||||
if(preg_match('/^[A-Za-z0-9_.]{3,16}$/', $username)) {
|
||||
if(strlen($password) > 3) {
|
||||
$db = MySQL_Database::instance();
|
||||
$test_username_sql = "SELECT COUNT(user_id) FROM " . MYSQL_PREFIX . "users
|
||||
WHERE username LIKE :username";
|
||||
$test_username = $db->prepare($test_username_sql);
|
||||
$test_username->execute(array(':username' => $username));
|
||||
if(!$test_username->fetchColumn()) {
|
||||
// hash the password
|
||||
$password = md5($password);
|
||||
$pw_str = substr($password, 0, 8);
|
||||
$password = $pw_str . md5($pw_str . $password);
|
||||
|
||||
$register_sql = "INSERT INTO " . MYSQL_PREFIX . "users
|
||||
(username, password, last_known_ip)
|
||||
VALUES(:username, :password, :ip)";
|
||||
$register = $db->prepare($register_sql);
|
||||
$is_registered = $register->execute(array(
|
||||
':username' => $username,
|
||||
':password' => $password,
|
||||
':ip' => ip2long($_SERVER['REMOTE_ADDR'])
|
||||
));
|
||||
|
||||
if($is_registered) {
|
||||
return array('r' => 'registered');
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'unknown');
|
||||
}
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'username taken');
|
||||
}
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'invalid password');
|
||||
}
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'invalid username');
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{Default_IM::}}}**{{{poll($method)}}}** ===
|
||||
//
|
||||
// Query the database for any new messages, and respond (or wait)
|
||||
// using the specified method (short, long, or comet).
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$method}}} is the type of response method to use as a reply.
|
||||
// See {{{im.js}}} for a description of each method.
|
||||
public function poll($method) {
|
||||
if(!$this->username)
|
||||
return array('r' => 'error', 'e' => 'no session found');
|
||||
|
||||
session_write_close(); // prevents locking
|
||||
|
||||
// If output buffering hasn't been setup yet...
|
||||
if(count(ob_list_handlers()) < 2) {
|
||||
// Buffer output, but flush always after any output
|
||||
ob_start();
|
||||
ob_implicit_flush(true);
|
||||
|
||||
// Set the headers such that our response doesn't get cached
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
}
|
||||
|
||||
switch($method) {
|
||||
case 'long':
|
||||
return $this->_longPoll();
|
||||
break;
|
||||
|
||||
case 'comet':
|
||||
return $this->_comet();
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'short':
|
||||
return $this->_shortPoll();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// === //private// {{{Default_IM::}}}**{{{_longPoll()}}}** ===
|
||||
//
|
||||
// Use the long polling technique to check for and deliver new messages.
|
||||
private function _longPoll() {
|
||||
set_time_limit(30);
|
||||
|
||||
// We're going to keep a running tally of the number of times
|
||||
// we've checked for, but haven't received, messages. As that
|
||||
// number increases, the sleep duration will increase.
|
||||
|
||||
$no_msg_count = 0;
|
||||
$start = time();
|
||||
do {
|
||||
$messages = Message::getMany('to', $this->user_id);
|
||||
|
||||
if(!$messages) {
|
||||
$no_msg_count++;
|
||||
sleep(2.5 + min($no_msg_count * 1.5, 7.5));
|
||||
}
|
||||
} while(!$messages && time() - $start < 30);
|
||||
|
||||
if($messages)
|
||||
return $this->_pollParseMessages($messages);
|
||||
else
|
||||
return array();
|
||||
}
|
||||
|
||||
// === //private// {{{Default_IM::}}}**{{{_shortPoll()}}}** ===
|
||||
//
|
||||
// Use the short polling technique to check for and deliver new messages.
|
||||
private function _shortPoll() {
|
||||
$messages = Message::getMany('to', $this->user_id);
|
||||
|
||||
if($messages) {
|
||||
return $this->_pollParseMessages($messages);
|
||||
} else {
|
||||
return array('r' => 'no messages');
|
||||
}
|
||||
}
|
||||
|
||||
// === //private// {{{Default_IM::}}}**{{{_comet()}}}** ===
|
||||
//
|
||||
// Use the comet/streaming technique to check for and deliver new messages.
|
||||
private function _comet() {
|
||||
set_time_limit(0);
|
||||
|
||||
// First, fix buffers
|
||||
echo str_repeat(chr(32), self::FIXBUFFER) . self::FIXEOL , ob_get_clean();
|
||||
|
||||
$no_msg_count = 0;
|
||||
while(true) {
|
||||
$messages = Message::getMany('to', $this->user_id);
|
||||
|
||||
if(!$messages) {
|
||||
$no_msg_count++;
|
||||
sleep(2.5 + min($no_msg_count * 1.5, 7.5));
|
||||
echo chr(32) , ob_get_clean();
|
||||
if(connection_aborted()) return;
|
||||
} else {
|
||||
$no_msg_count = 0;
|
||||
echo '<script type="text/javascript">parent.AjaxIM.incoming(' .
|
||||
json_encode($this->_pollParseMessages($messages)) .
|
||||
');</script>' . self::FIXEOL , ob_get_clean();
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === //private// {{{Default_IM::}}}**{{{_pollParseMessages()}}}** ===
|
||||
//
|
||||
// Parse each message object and return it as an array deliverable to the client.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$messages}}} is the array of message objects.
|
||||
private function _pollParseMessages($messages) {
|
||||
$msg_arr = array();
|
||||
foreach($messages as $msg) {
|
||||
$msg_arr[] = array('t' => $msg->type, 's' => $msg->from, 'r' => $msg->to, 'm' => $msg->message);
|
||||
}
|
||||
return $msg_arr;
|
||||
}
|
||||
|
||||
// === //private// {{{Default_IM::}}}**{{{_sanitize()}}}** ===
|
||||
//
|
||||
// Sanitize messages by preventing any HTML tags from being created
|
||||
// (replaces < and > entities).
|
||||
private function _sanitize($str) {
|
||||
return str_replace('>', '>', str_replace('<', '<', str_replace('&', '&', $str)));
|
||||
}
|
||||
}
|
||||
|
||||
/* End of libraries/server/Default.php */
|
||||
@@ -1,170 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Library: NodeJS
|
||||
* Author: Joshua Gross
|
||||
* Version: 0.1 alpha
|
||||
* Date: February 12, 2010
|
||||
*
|
||||
* Description:
|
||||
* A library for the Node.js server. It will authenticate a
|
||||
* user against the default database, and then add the user to
|
||||
* the server.
|
||||
*
|
||||
* Requirements: Database, Node.js
|
||||
*/
|
||||
|
||||
// == Node.js Server Library ==
|
||||
//
|
||||
// This is the [[http://nodejs.org|Node.js]] server library for Ajax IM. It
|
||||
// handles registration and passing login to the Node.js server.
|
||||
class NodeJS_IM extends IM {
|
||||
// === {{{NodeJS_IM::}}}**{{{__construct()}}}** ===
|
||||
//
|
||||
// Initializes the IM library and retrieves the user's session, if one
|
||||
// currently exists.
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
global $nodejs_memcache_server;
|
||||
|
||||
if(!is_array($nodejs_memcache_server))
|
||||
die(json_encode(array('r' => 'error', 'e' => 'server misconfigured')));
|
||||
|
||||
$this->memcache = memcache_connect($nodejs_memcache_server[0], $nodejs_memcache_server[1]);
|
||||
|
||||
$cookie = json_decode($_COOKIE[COOKIE_NAME]);
|
||||
if($cookie) {
|
||||
$session = json_decode($this->memcache->get('session/' . $cookie->sid));
|
||||
if($session) {
|
||||
$this->username = $session->username;
|
||||
$this->user_id = $session->user_id;
|
||||
$this->server_key = $session->server_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{NodeJS_IM::}}}**{{{__destruct()}}}** ===
|
||||
//
|
||||
// Closes the connection to the Node.js server.
|
||||
function __destruct() {
|
||||
$this->memcache->close();
|
||||
}
|
||||
|
||||
// === {{{NodeJS_IM::}}}**{{{login($username, $password)}}}** ===
|
||||
//
|
||||
// Authenticate a user against the database. If the user is valid,
|
||||
// pass the user's information to the Node.js server.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$username}}} is the user's login name.\\
|
||||
// * {{{$password}}} is an already-md5'd copy of the user's password.
|
||||
public function login($username, $password) {
|
||||
if($user = User::authenticate($username, $password)) {
|
||||
// user just logged in, update login time.
|
||||
$user->lastLogin(time());
|
||||
|
||||
$session_id = md5(microtime(true) . $user->username);
|
||||
$session = array(
|
||||
'username' => $user->username,
|
||||
'user_id' => intval($user->user_id),
|
||||
'session_id' => $session_id,
|
||||
'friends' => Friend::of($user->user_id, true)
|
||||
);
|
||||
|
||||
$cookie = json_encode(array(
|
||||
'user' => $user->username,
|
||||
'sid' => $session_id
|
||||
));
|
||||
setcookie(COOKIE_NAME, $cookie, time()+(60*60*24*COOKIE_PERIOD), '/', COOKIE_DOMAIN);
|
||||
$this->memcache->add($user->username, json_encode($session));
|
||||
|
||||
return array('r' => 'logged in', 's' => $session_id, 'f' => $session['friends']);
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'invalid user');
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{NodeJS_IM::}}}**{{{logout()}}}** ===
|
||||
//
|
||||
// Signs the user out of the Node.js server.
|
||||
public function logout() {
|
||||
$this->memcache->delete($this->username);
|
||||
|
||||
return array('r' => 'logged out');
|
||||
}
|
||||
|
||||
// === {{{NodeJS_IM::}}}**{{{register($username, $password)}}}** ===
|
||||
//
|
||||
// Create a new user based on the provided username and password.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$username}}} is the new user's login name.\\
|
||||
// * {{{$password}}} is the user's plaintext password.
|
||||
public function register($username, $password) {
|
||||
if(preg_match('/^[A-Za-z0-9_.]{3,16}$/', $username)) {
|
||||
if(strlen($password) > 3) {
|
||||
$db = MySQL_Database::instance();
|
||||
$test_username_sql = "SELECT COUNT(user_id) FROM " . MYSQL_PREFIX . "users
|
||||
WHERE username LIKE :username";
|
||||
$test_username = $db->prepare($test_username_sql);
|
||||
$test_username->execute(array(':username' => $username));
|
||||
if(!$test_username->fetchColumn()) {
|
||||
// hash the password
|
||||
$password = md5($password);
|
||||
$pw_str = substr($password, 0, 8);
|
||||
$password = $pw_str . md5($pw_str . $password);
|
||||
|
||||
$register_sql = "INSERT INTO " . MYSQL_PREFIX . "users
|
||||
(username, password, last_known_ip)
|
||||
VALUES(:username, :password, :ip)";
|
||||
$register = $db->prepare($register_sql);
|
||||
$is_registered = $register->execute(array(
|
||||
':username' => $username,
|
||||
':password' => $password,
|
||||
':ip' => ip2long($_SERVER['REMOTE_ADDR'])
|
||||
));
|
||||
|
||||
if($is_registered) {
|
||||
return array('r' => 'registered');
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'unknown');
|
||||
}
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'username taken');
|
||||
}
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'invalid password');
|
||||
}
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'invalid username');
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{NodeJS_IM::}}}**{{{add_friend($friend, $group)}}}** ===
|
||||
//
|
||||
// Add a new friend to the current user's friend list, in the specified
|
||||
// group name. Adds the friend to both the database and the current
|
||||
// Node.js server session.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$friend}}} is the username of the friend.\\
|
||||
// * {{{$group}}} is the name of group in which to place the friend.
|
||||
public function add_friend($friend, $group) {
|
||||
if(!$this->username)
|
||||
return array('r' => 'error', 'e' => 'no session found');
|
||||
|
||||
$friend = new Friend($this->username, $friend, $group);
|
||||
$friend_obj_id = $friend->save();
|
||||
|
||||
if($friend_obj_id) {
|
||||
$friend_arr = Friend::get($friend_obj_id);
|
||||
$this->memcache->set('user/' . $this->username . '/friends/add', json_encode($friend_arr));
|
||||
|
||||
return array('r' => 'added');
|
||||
} else {
|
||||
return array('r' => 'error', 'e' => 'invalid user');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* End of libaries/server/NodeJS.php */
|
||||
@@ -1,117 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Library: NodeJS (Guests)
|
||||
* Author: Joshua Gross
|
||||
* Version: 0.1 alpha
|
||||
* Date: February 12, 2010
|
||||
*
|
||||
* Description:
|
||||
* A library for the Node.js server. It does not require any
|
||||
* sort of database installation, and will automatically create
|
||||
* "Guest" users, making it possible to automatically sign on
|
||||
* individuals without any sort of registration.
|
||||
*
|
||||
* Requirements: Node.js
|
||||
*/
|
||||
|
||||
// == Node.js Guest Server Library ==
|
||||
//
|
||||
// This is the [[http://nodejs.org|Node.js]] Guest server library for Ajax IM. It
|
||||
// creates a random "Guest" username upon login and passes that information to
|
||||
// the Node.js server. Additionally, it makes every user a friend of every other
|
||||
// user.
|
||||
class NodeJS_Guests_IM extends IM {
|
||||
// === {{{NodeJS_Guests_IM::}}}**{{{__construct()}}}** ===
|
||||
//
|
||||
// Initializes the IM library and retrieves the user's session, if one
|
||||
// currently exists.
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
global $nodejs_memcache_server;
|
||||
|
||||
if(!is_array($nodejs_memcache_server))
|
||||
die(json_encode(array('r' => 'error', 'e' => 'server misconfigured')));
|
||||
|
||||
$this->memcache = memcache_connect($nodejs_memcache_server[0], $nodejs_memcache_server[1]);
|
||||
|
||||
$cookie = json_decode($_COOKIE[COOKIE_NAME]);
|
||||
if($cookie) {
|
||||
$session = json_decode($this->memcache->get('session/' . $cookie->sid));
|
||||
if($session) {
|
||||
$this->username = $session->username;
|
||||
$this->user_id = $session->user_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === {{{NodeJS_Guests_IM::}}}**{{{__destruct()}}}** ===
|
||||
//
|
||||
// Closes the connection to the Node.js server.
|
||||
function __destruct() {
|
||||
$this->memcache->close();
|
||||
}
|
||||
|
||||
// === {{{NodeJS_IM::}}}**{{{login($username, $password)}}}** ===
|
||||
//
|
||||
// Create a new Guest username based in the microtime, then
|
||||
// pass the user's information to the Node.js server.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{$username}}} is unused (kept for compatability with caller).\\
|
||||
// * {{{$password}}} is unused (kept for compatability with caller).
|
||||
public function login($username='', $password='') {
|
||||
$username = 'Guest' . (microtime(true) * 100);
|
||||
$session_id = md5(microtime(true) . $username);
|
||||
|
||||
$friends_raw = json_decode($this->memcache->get('list/'));
|
||||
$friends = array();
|
||||
foreach($friends_raw as $friend) {
|
||||
$friends[] = array(
|
||||
'u' => $friend->username,
|
||||
's' => $friend->status,
|
||||
'g' => 'Users'
|
||||
);
|
||||
}
|
||||
|
||||
$session = array(
|
||||
'username' => $username,
|
||||
'user_id' => $username,
|
||||
'session_id' => $session_id,
|
||||
'friends' => $friends,
|
||||
'guest' => true
|
||||
);
|
||||
|
||||
$cookie = json_encode(array(
|
||||
'user' => $username,
|
||||
'sid' => $session_id
|
||||
));
|
||||
setcookie(COOKIE_NAME, $cookie, time()+(60*60*24*COOKIE_PERIOD), '/', COOKIE_DOMAIN);
|
||||
$this->memcache->add($username, json_encode($session));
|
||||
$this->memcache->set('from/' . $username . '/broadcast/raw', json_encode(array(
|
||||
't' => 's',
|
||||
's' => $username,
|
||||
'r' => 'all',
|
||||
'm' => '1:',
|
||||
'g' => 'Users'
|
||||
)));
|
||||
|
||||
return array(
|
||||
'r' => 'logged in',
|
||||
'u' => $username,
|
||||
's' => $session_id,
|
||||
'f' => $session['friends']
|
||||
);
|
||||
}
|
||||
|
||||
// === {{{NodeJS_IM::}}}**{{{logout()}}}** ===
|
||||
//
|
||||
// Signs the user out of the Node.js server.
|
||||
public function logout() {
|
||||
$this->memcache->delete($this->username);
|
||||
|
||||
return array('r' => 'logged out');
|
||||
}
|
||||
}
|
||||
|
||||
/* End of libraries/server/NodeJS.php */
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
class IM {
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
public function login($username, $password) {
|
||||
return array('e'=>'not implemented');
|
||||
}
|
||||
|
||||
public function logout() {
|
||||
return array('e'=>'not implemented');
|
||||
}
|
||||
|
||||
public function send($to, $message) {
|
||||
return array('e'=>'not implemented');
|
||||
}
|
||||
|
||||
public function status($status, $message) {
|
||||
return array('e'=>'not implemented');
|
||||
}
|
||||
|
||||
public function poll($method) {
|
||||
}
|
||||
}
|
||||
|
||||
/* End of libraries/server/base.php */
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env node
|
||||
var http = require('http'),
|
||||
url = require('url'),
|
||||
fs = require('fs'),
|
||||
io = require('socket.io'),
|
||||
uglifyjs,
|
||||
o_ = require('./libs/utils'),
|
||||
client = {
|
||||
'im.js': {
|
||||
folder: 'js',
|
||||
files: [
|
||||
'intro.js',
|
||||
'cookies.js', 'dateformat.js', 'json.js',
|
||||
'autogrow.js', 'md5.js', 'store.js',
|
||||
'xxtea.js', 'templates.js', 'im.js',
|
||||
'l10n.js', 'outro.js'
|
||||
],
|
||||
},
|
||||
'theme.css': {
|
||||
folder: 'themes/default',
|
||||
files: ['theme.css']
|
||||
}
|
||||
},
|
||||
contentTypes = {
|
||||
js: 'text/javascript',
|
||||
css: 'text/css'
|
||||
},
|
||||
_clientFiles = {},
|
||||
server, socket;
|
||||
try { var uglifyjs = require('uglify-js'); } catch(e) {}
|
||||
|
||||
o_.merge(global, require('./settings'));
|
||||
try { o_.merge(global, require('./settings.local')); } catch(e) {}
|
||||
|
||||
server = http.createServer(_serveClient);
|
||||
server.listen(APP_PORT, APP_HOST);
|
||||
|
||||
var authHandler = require('./auth/' + AUTH_LIBRARY)(),
|
||||
sessionStore = require('./session/' + SESSION_STORE)(),
|
||||
msgHandler = require('./message/' + MESSAGE_HANDLER)(authHandler, sessionStore);
|
||||
|
||||
// setup socket.io
|
||||
socket = io.listen(server);
|
||||
socket.on('connection', function(client) {
|
||||
client.on('message', function(message) {
|
||||
msgHandler.message(client, message);
|
||||
});
|
||||
|
||||
client.on('disconnect', function() {
|
||||
msgHandler.disconnect(client, SESSION_TIMEOUT);
|
||||
});
|
||||
});
|
||||
|
||||
// compile client javascript
|
||||
for(var file in client) {
|
||||
var fileData = "";
|
||||
for(var i = 0, fl = client[file].files.length; i < fl; i++)
|
||||
fileData += fs.readFileSync(
|
||||
__dirname + '/../client/'
|
||||
+ client[file].folder + '/'
|
||||
+ client[file].files[i],
|
||||
'utf8'
|
||||
);
|
||||
var ext = file.split('.').pop();
|
||||
/*
|
||||
// breaks on xxtea library?
|
||||
if(ext == 'js' && uglifyjs) {
|
||||
// if uglify-js is installed, let's compress
|
||||
fileData = uglifyjs.parser.parse(fileData);
|
||||
fileData = uglifyjs.uglify.ast_mangle(fileData);
|
||||
fileData = uglifyjs.uglify.ast_squeeze(fileData);
|
||||
fileData = uglifyjs.uglify.gen_code(fileData);
|
||||
}
|
||||
*/
|
||||
_clientFiles[file] = {
|
||||
headers: {
|
||||
'Content-Length': fileData.length,
|
||||
'Content-Type': contentTypes[ext]
|
||||
// Should use ETag
|
||||
},
|
||||
content: fileData,
|
||||
encoding: 'utf8'
|
||||
};
|
||||
};
|
||||
|
||||
// serve client javascript
|
||||
function _serveClient(req, res) {
|
||||
var path = url.parse(req.url).pathname,
|
||||
file = path.substr(1);
|
||||
|
||||
if(req.method == 'GET' && file in _clientFiles) {
|
||||
res.writeHead(200, _clientFiles[file].headers);
|
||||
res.end(_clientFiles[file].content, _clientFiles[file].encoding);
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end('404');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
var ExampleAuth = function() {};
|
||||
|
||||
ExampleAuth.prototype.authenticate = function(client, msg, callback) {
|
||||
callback({
|
||||
authenticated: true,
|
||||
username: 'username' + Math.round(Math.random() * 3)
|
||||
});
|
||||
};
|
||||
|
||||
ExampleAuth.prototype.friends = function(client, res, callback) {
|
||||
callback(['username0', 'username1', 'username2', 'username3']);
|
||||
};
|
||||
|
||||
var instance = new ExampleAuth();
|
||||
module.exports = function getInstance() {
|
||||
return instance;
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
// == Node.js Server Configuration ==
|
||||
//
|
||||
// This is the configuration file for the Node.js Ajax IM server. Here, you
|
||||
// can set which ports will be used for the public and internal servers,
|
||||
// as well as other settings such as the session cookie name and expiration.
|
||||
|
||||
// === {{{ ports }}} ===
|
||||
//
|
||||
// Define the ports and hosts that Ajax IM will run on. {{{public}}} is the
|
||||
// public-facing API, while {{{private}}} is the memcache-compatible API
|
||||
// intended for server-side use.
|
||||
//
|
||||
// The first item of each array is the port, the second is the host name. If
|
||||
// you do not want to specify a host, set it to {{{null}}}.
|
||||
exports.ports = {
|
||||
public: [8000, 'localhost'],
|
||||
private: [11998, 'localhost']
|
||||
};
|
||||
|
||||
// === {{{ cookie }}} ===
|
||||
//
|
||||
// Define the cookie name and how long a session will be stored on the server.
|
||||
// If you change the cookie name here, you will also need to change it in the
|
||||
// PHP login script/configuration; if you fail to do this, the Node.js server
|
||||
// will be looking for a cookie that does not exist.
|
||||
//
|
||||
// **Note:** The period for the session should be as long or longer than the
|
||||
// cookie itself is set to be stored. If it is less, the user won't be logged
|
||||
// back in automatically, as their cookie will have been deleted.
|
||||
exports.cookie = {
|
||||
name: 'ajaxim_session',
|
||||
period: 8760
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
// Many functions borrowed from
|
||||
// ext.js - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
module.exports = o_ = {
|
||||
merge: function(a, b) {
|
||||
if(!b) return a;
|
||||
var keys = Object.keys(b);
|
||||
for(var i = 0, len = keys.length; i < len; i++)
|
||||
a[keys[i]] = b[keys[i]];
|
||||
return a;
|
||||
},
|
||||
|
||||
values: function(obj) {
|
||||
if(typeof obj == 'array')
|
||||
return obj;
|
||||
if(!obj || typeof obj !== 'object')
|
||||
return [];
|
||||
var keys = Object.keys(obj),
|
||||
vals = [];
|
||||
for(var i = 0, len = keys.length; i < len; ++i)
|
||||
vals.push(obj[keys[i]]);
|
||||
return vals;
|
||||
},
|
||||
|
||||
find: function(arr, fn, context) {
|
||||
if(typeof arr == 'array') {
|
||||
for(var i = 0, len = arr.length; i < len; ++i)
|
||||
if(fn.call(context, arr[i], i, arr))
|
||||
return arr[i];
|
||||
} else if(typeof arr == 'object') {
|
||||
var keys = Object.keys(obj);
|
||||
for(var i = 0, len = keys.length; i < len; ++i)
|
||||
if(fn.call(context, arr[keys[i]], keys[i], arr))
|
||||
return [arr[keys[i]], keys[i]];
|
||||
}
|
||||
},
|
||||
|
||||
bind: function(fn, context) {
|
||||
var self = fn,
|
||||
args = Array.prototype.slice.call(arguments, 2);
|
||||
return function() {
|
||||
var _args = Array.prototype.slice.call(arguments);
|
||||
return fn.apply(context, args.concat(_args));
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
var MessageHandler = function(auth_handler, session_store) {
|
||||
this.auth_handler = auth_handler;
|
||||
this.session_store = session_store;
|
||||
};
|
||||
|
||||
MessageHandler.prototype._auth = function(client, message) {
|
||||
var session = this.session_store.get('identifier', message.identifier);
|
||||
|
||||
if(session) {
|
||||
this.session_store.touch(session, client);
|
||||
client.send({
|
||||
type: 'AUTH',
|
||||
loggedin: true,
|
||||
username: session.username
|
||||
});
|
||||
} else {
|
||||
var auth_handler = this.auth_handler,
|
||||
session_store = this.session_store;
|
||||
|
||||
// authenticate the new user
|
||||
auth_handler.authenticate(client, message, function(res) {
|
||||
|
||||
// authentication succeeded, setup user
|
||||
if(res.authenticated) {
|
||||
|
||||
// get user's friends
|
||||
auth_handler.friends(client, res, function(friends_array) {
|
||||
var friends_list = {},
|
||||
sess,
|
||||
indentifier;
|
||||
|
||||
for(var i = 0, fl = friends_array.length; i < fl; i++) {
|
||||
sess = session_store.get('username', friends_array[i])
|
||||
|
||||
if(sess) {
|
||||
// notify friends that user has logged on
|
||||
sess.client.send({
|
||||
type: 'STATUS',
|
||||
username: res.username,
|
||||
status: 'online'
|
||||
});
|
||||
|
||||
friends_list[friends_array[i]] = sess.status;
|
||||
} else if(friends_array[i] == res.username) {
|
||||
friends_list[friends_array[i]] = 'online';
|
||||
} else {
|
||||
friends_list[friends_array[i]] = 'offline';
|
||||
}
|
||||
}
|
||||
|
||||
identifier = session_store.create(res.username,
|
||||
client,
|
||||
friends_array
|
||||
);
|
||||
|
||||
// notify user that they're logged on
|
||||
client.send({
|
||||
type: 'AUTH',
|
||||
loggedin: true,
|
||||
username: res.username,
|
||||
friends: friends_list,
|
||||
identifier: identifier
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// authentication failed
|
||||
client.send({
|
||||
type: 'AUTH',
|
||||
loggedin: false
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MessageHandler.prototype._im = function(client, to, message) {
|
||||
var sender = this.session_store.get('client', client.sessionId),
|
||||
recipient;
|
||||
|
||||
if(sender) {
|
||||
recipient = this.session_store.get('username', to);
|
||||
|
||||
if(recipient) {
|
||||
recipient.client.send({
|
||||
type: 'IM',
|
||||
from: sender.username,
|
||||
message: message
|
||||
});
|
||||
} else {
|
||||
client.send({
|
||||
type: 'ERROR',
|
||||
origin: 'IM',
|
||||
to: to
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandler.prototype._status = function(client, status, status_msg) {
|
||||
var session = this.session_store.get('client', client.sessionId),
|
||||
friend;
|
||||
|
||||
if(['online', 'away', 'offline'].indexOf(status) != -1) {
|
||||
session.status = status;
|
||||
this.session_store.set(session.username, session);
|
||||
|
||||
// let user's friends know about the new status
|
||||
for(var i = 0, fl = session.friends.length; i < fl; i++) {
|
||||
friend = this.session_store.get('username', session.friends[i]);
|
||||
|
||||
if(friend) {
|
||||
friend.client.send({
|
||||
type: 'STATUS',
|
||||
username: session.username,
|
||||
status: status
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandler.prototype._disconnect = function(client, SESSION_TIMEOUT) {
|
||||
var threshold = +new Date - SESSION_TIMEOUT,
|
||||
session = this.session_store.get('client', client);
|
||||
|
||||
if(session && session.lastAccess < threshold) {
|
||||
if(session.status != 'offline')
|
||||
this._status(session.username, 'offline');
|
||||
|
||||
this.session_store.remove(session.username);
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandler.prototype.message = function(client, message) {
|
||||
console.log(message);
|
||||
switch(message.type) {
|
||||
case 'AUTH':
|
||||
this._auth(client, message);
|
||||
break;
|
||||
|
||||
case 'IM':
|
||||
this._im(client, message.to, message.message);
|
||||
break;
|
||||
|
||||
case 'STATUS':
|
||||
this._status(client, message.status, message.status_msg);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandler.prototype.disconnect = function(client, SESSION_TIMEOUT) {
|
||||
var self = this;
|
||||
setTimeout(function() {
|
||||
self._disconnect(client, SESSION_TIMEOUT);
|
||||
}, SESSION_TIMEOUT);
|
||||
};
|
||||
|
||||
module.exports = function createInstance(auth_handler, session_store) {
|
||||
return new MessageHandler(auth_handler, session_store);
|
||||
};
|
||||
@@ -1,902 +0,0 @@
|
||||
// = Ajax IM =
|
||||
//
|
||||
// **Copyright © 2005 – 2010 Joshua Gross**\\
|
||||
// //MIT Licensed//
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// == Node.js Server ==
|
||||
//
|
||||
// This is the [[http://nodejs.org|node.js]] server for Ajax IM. It handles
|
||||
// user state and passing communications amongst users, though it still
|
||||
// relies on outside assistance for initial authentication. It is recommended
|
||||
// that this server be used, rather than the PHP-only server script.
|
||||
var sys = require('sys'),
|
||||
http = require('http'),
|
||||
tcp = require('tcp'),
|
||||
url = require('url'),
|
||||
config = require('./config');
|
||||
|
||||
var AjaxIM = function(config) {
|
||||
var self = this;
|
||||
|
||||
// === {{{ public variables }}} ===
|
||||
//
|
||||
// There are a few public class variables that can be accessed
|
||||
// if need be.
|
||||
//
|
||||
// * {{{users}}} is a hash table of all active users, by username. Each active
|
||||
// user has their own hash table containing state and connection information:
|
||||
// ** {{{username}}} is the identifying name of the user. It is the same as the hash key.
|
||||
// ** {{{user_id}}} is the database-stored "id" of the user.
|
||||
// ** {{{session_id}}} is the session identifier that the user uses to communicate with the server. It is stored in a cookie on the client-side.
|
||||
// ** {{{last_active}}} is a Unix timestamp signifying when the user last executed a server-side action. It is used to determine when a user has become inactive.
|
||||
// ** {{{friends}}} is the list of the user's friends, as provided on login. Follows the same format as is outlined in {{{im.js}}}
|
||||
// ** {{{status}}} is the user's status, in the format {{{ {s: 0, m: 'message'}, }}} where {{{s}}} is the status code, and {{{m}}} is the status message.
|
||||
// ** {{{callback}}} is the function that will send data back to this user through any open connections. Should this user not be connected at that moment, messages are queued.
|
||||
// * {{{sessions}}} is a hash table of session ids with usernames as values.
|
||||
// * {{{debug}}} enables or disables debug messages from being output to the console. Set manually.
|
||||
// * {{{onlineCount}}} contains the number of users online at any one moment.
|
||||
this.users = {};
|
||||
this.sessions = {};
|
||||
|
||||
this.config = config;
|
||||
|
||||
this.debug = true;
|
||||
|
||||
this.onlineCount = 0;
|
||||
|
||||
// === {{{ AjaxIM.init() }}} ===
|
||||
//
|
||||
// Initializes the frontend webserver and the backend Memcache server, which provides
|
||||
// and easy-to-use API for controlling the server from other scripts.
|
||||
this.init = function() {
|
||||
if(typeof this.config.ports.public[0] != 'number' ||
|
||||
typeof this.config.ports.private[0] != 'number')
|
||||
throw new TypeError();
|
||||
this.server = new WebServer(this.config.ports.public[1],
|
||||
this.config.ports.public[0]);
|
||||
|
||||
this.server.get('^/poll$', this.poll);
|
||||
this.server.get('^/send$', this.send);
|
||||
this.server.get('^/status$', this.status);
|
||||
this.server.get('^/resume$', this.resume);
|
||||
this.server.get('^/online$', this.online);
|
||||
this.server.start();
|
||||
|
||||
this.internalServer =
|
||||
new MemcacheServer(this.config.ports.private[1], this.config.ports.private[0]);
|
||||
this.internalServer.login(function(username, data) {
|
||||
self.apiLogin.apply(self, [username, data]);
|
||||
});
|
||||
this.internalServer.getUser(function(cmd) {
|
||||
var actions = {
|
||||
'session': self.apiGetSession,
|
||||
'username': self.apiGetUser,
|
||||
'list': self.apiGetUserList,
|
||||
'online': self.apiOnlineCount
|
||||
};
|
||||
|
||||
var parts = cmd.split('/');
|
||||
|
||||
if(parts[0] in actions) {
|
||||
if(parts.length > 1 && parts[1].length > 0)
|
||||
return actions[parts[0]].apply(self, parts.slice(1));
|
||||
else
|
||||
return actions[parts[0]].apply(self);
|
||||
}
|
||||
});
|
||||
this.internalServer.logout(function(username) {
|
||||
return (self._killUser.call(self, username) !== false);
|
||||
});
|
||||
this.internalServer.setCustom('friends/add', function(type, id, value) {
|
||||
self.apiAddFriend.apply(self, [type, id, value]);
|
||||
});
|
||||
this.internalServer.setCustom('friends/remove', function(type, id, value) {
|
||||
self.apiRemoveFriend.apply(self, [type, id, value]);
|
||||
});
|
||||
this.internalServer.setCustom('broadcast/message', function(type, id, value) {
|
||||
self.apiBroadcastMessage.apply(self, [type, id, value]);
|
||||
});
|
||||
this.internalServer.setCustom('broadcast/raw', function(type, id, value) {
|
||||
self.apiBroadcastRaw.apply(self, [type, id, value]);
|
||||
});
|
||||
this.internalServer.start();
|
||||
|
||||
this._start_gc();
|
||||
}
|
||||
|
||||
// === //private//\\ {{{ AjaxIM._start_gc() }}} ===
|
||||
//
|
||||
// The "garbage collector," which is run every 5 seconds. It signs out
|
||||
// users which are no longer active and have no active connections. Those
|
||||
// users that have not been active for 50 or more seconds but have an active
|
||||
// connection have their connections reset (to prevent them from automatically
|
||||
// timing out).
|
||||
this._start_gc = function() {
|
||||
// Remove inactive users (> 30 seconds of inactivity & no callbacks)
|
||||
setInterval(function() {
|
||||
for(username in self.users) {
|
||||
var user = self.users[username];
|
||||
if((Date.now() - user.last_active) > 50000 &&
|
||||
user._callbacks.length == 0) {
|
||||
|
||||
self._killUser(username);
|
||||
} else if((Date.now() - user.last_active) >= 50000) {
|
||||
user.callback();
|
||||
}
|
||||
}
|
||||
}, 5000); // 5 seconds
|
||||
|
||||
// Remove old sessions after session expiry period
|
||||
setInterval(function() {
|
||||
for(s in self.sessions) {
|
||||
var session = self.sessions[s];
|
||||
var age = (Date.now() - session.last_active) / 3600000; // in hours
|
||||
|
||||
if(age >= self.config.cookie.period) {
|
||||
self._d('Session [' + s + '] for user [' +
|
||||
session.username + '] has expired and has been discarded.');
|
||||
delete self.sessions[s];
|
||||
}
|
||||
}
|
||||
}, 3600000); // 1 hour
|
||||
};
|
||||
|
||||
// === //private//\\ {{{ AjaxIM._session(request, provide) }}} ===
|
||||
//
|
||||
// Returns the requested session information, {{{provide}}}, based on
|
||||
// the given request object, {{{request}}}. If the session does not
|
||||
// exist, {{{false}}} is returned. If {{{'object'}}} is requested and
|
||||
// there is no active session for that user, {{{false}}} is also returned.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{request}}} is a request object, as is provided by the {{{http}}} module
|
||||
// * {{{provide}}} is one of:
|
||||
// ** {{{username}}} which merely checks for the existence of session data and
|
||||
// returns the username for that session.
|
||||
// ** {{{object}}} checks for the existence of the session //and// the existence of an active session (the user is currently logged in), and returns the user object, if found.
|
||||
this._session = function(request, provide) {
|
||||
if(this.config.cookie.name in request.cookies) {
|
||||
try {
|
||||
var session_id = request.cookies[this.config.cookie.name].sid;
|
||||
|
||||
if(session_id in this.sessions) {
|
||||
var session = this.sessions[session_id];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch(e) {
|
||||
this._d('Invalid cookie for an unknown user.');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(provide) {
|
||||
case 'username':
|
||||
return session.username;
|
||||
break;
|
||||
|
||||
case 'session':
|
||||
return session;
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
if(session.username in this.users) {
|
||||
return this.users[session.username];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// === //private//\\ {{{ AjaxIM._d(str) }}} ===
|
||||
//
|
||||
// If debugging is enabled, this function prints debug data, with the
|
||||
// current formatted time, to the console.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{str}}} is the debug string.
|
||||
this._d = function(str) {
|
||||
if(this.debug) {
|
||||
var addZero = function(str) { return str < 10 ? '0' + str : str; };
|
||||
|
||||
var d = new Date();
|
||||
var d_str = addZero(d.getMonth() + 1) + '/' + addZero(d.getDate()) + '/' + d.getFullYear() + ' ' +
|
||||
addZero(d.getHours()) + ':' + addZero(d.getMinutes()) + ':' + addZero(d.getSeconds());
|
||||
|
||||
sys.puts('[' + d_str + '] ' + str);
|
||||
}
|
||||
};
|
||||
|
||||
// === //private//\\ {{{ AjaxIM._initUser(username, data) }}} ===
|
||||
//
|
||||
// Initializes a user session and adds the user to the users list. Additionally,
|
||||
// it stores a callback function which will push data directly to this user. A
|
||||
// "session" object is also stored, allowing a user to reconnect at a later time
|
||||
// (provided that their session hasn't yet expired).
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{username}}} is the user's account name\\
|
||||
// * {{{data}}} is an array of user data:\\
|
||||
// ** {{{user_id}}} is a unique id for this particular user.
|
||||
// ** {{{session_id}}} is the user's unique session id (used to (re)connect to the server).
|
||||
// ** {{{friends}}} is the friends list.
|
||||
// ** {{{guest}}} (optional) defines whether or not this is a "guest" (temporary) user.
|
||||
this._initUser = function(username, data) {
|
||||
if(data['user_id']) {
|
||||
self._d('User [' + username + '] has connected. Adding to user hash and notifying friends.');
|
||||
|
||||
if(!(username in self.users)) {
|
||||
self.users[username] = {
|
||||
username: username,
|
||||
user_id: data.user_id,
|
||||
session_id: data.session_id,
|
||||
last_active: Date.now(),
|
||||
friends: data.friends,
|
||||
status: {s: 1, m: ''},
|
||||
guest: data['guest'] ? true : false,
|
||||
callback: function(msg) {
|
||||
var cbs = self.users[username]._callbacks;
|
||||
if(msg)
|
||||
self.users[username]._queue.push(msg);
|
||||
|
||||
if(cbs.length) {
|
||||
for(var i = 0; i < cbs.length; i++)
|
||||
cbs[i](self.users[username]._queue);
|
||||
self.users[username]._callbacks = [];
|
||||
self.users[username]._queue = [];
|
||||
} else {
|
||||
if(self.users[username]._callbackAttempts < 3) {
|
||||
setTimeout(function() {
|
||||
try {
|
||||
self.users[username].callback();
|
||||
self.users[username]._callbackAttempts++;
|
||||
} catch(e) {}
|
||||
}, 150);
|
||||
} else {
|
||||
self.users[username]._callbackAttempts = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
_callbacks: [],
|
||||
_queue: [],
|
||||
_callbackAttempts: 0
|
||||
};
|
||||
self.onlineCount++;
|
||||
}
|
||||
|
||||
self.sessions[data.session_id] = {
|
||||
username: username,
|
||||
user_id: data.user_id,
|
||||
friends: data.friends,
|
||||
last_active: Date.now()
|
||||
};
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// === //private//\\ {{{ AjaxIM._killUser() }}} ===
|
||||
//
|
||||
// Remove a user from the server. If they are a guest user, also remove
|
||||
// their session data; otherwise, retain session data so that the user's
|
||||
// session can be resumed later.
|
||||
this._killUser = function(username) {
|
||||
if(!username || !(username in self.users))
|
||||
return false;
|
||||
|
||||
var user = self.users[username];
|
||||
if(user['guest']) {
|
||||
// User was a guest, so notify everyone that they logged off and remove their session.
|
||||
self.apiBroadcastRaw('from', username, {t: 's', s: username, r: '', m: '0:'});
|
||||
delete self.sessions[self.users[username].session_id];
|
||||
} else {
|
||||
user.friends.forEach(function(f) {
|
||||
if(f.u in self.users) {
|
||||
for(var i=0; i < self.users[f.u]['friends'].length; i++) {
|
||||
if(self.users[f.u]['friends'][i].u == username && self.users[f.u]['friends'][i].u != user.username) {
|
||||
self.users[f.u]['friends'][username].s = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.users[f.u].callback({t: 's', s: username, r: f.u, m: '0:'});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
user.callback();
|
||||
|
||||
self._d('User [' + username + '] has disconnected. Notifying friends and removing from user hash.');
|
||||
delete self.users[username];
|
||||
self.onlineCount--;
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.poll() }}} ===
|
||||
//
|
||||
// Store the user's connection in the callback list and update both the
|
||||
// user and session "last_active" variable (denoting when the user last
|
||||
// contacted the server).
|
||||
this.poll = function() {
|
||||
if(this.debug && ('sid' in this.request.uri.params && this.request.uri.params['sid'].length))
|
||||
this.request.cookies[self.config.cookie.name] = {sid: this.request.uri.params.sid};
|
||||
|
||||
if(!(user = self._session(this.request, 'object'))) {
|
||||
var r = this;
|
||||
self._d('User with session id [' + ('ajaxim_session' in this.request.cookies ?
|
||||
this.request.cookies.ajaxim_session['sid'] :
|
||||
'unknown'
|
||||
) + '] tried to connect. No/invalid session found.');
|
||||
r.response.reply(200, {'r': 'error', 'e': 'no session found'});
|
||||
} else {
|
||||
var response = this.response;
|
||||
|
||||
user._callbacks.push(function(msg) { response.reply(200, msg); });
|
||||
self.users[user.username].last_active = Date.now();
|
||||
self.sessions[user.session_id].last_active = Date.now();
|
||||
}
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.resume() }}} ===
|
||||
//
|
||||
// Attempt to resume a session based on a session id stored in a
|
||||
// cookie.
|
||||
this.resume = function() {
|
||||
if(!(user = self._session(this.request, 'username'))) {
|
||||
this.response.reply(200, {'r': 'error', 'e': 'no session found'});
|
||||
} else {
|
||||
if(!(user in self.users)) {
|
||||
session = self._session(this.request, 'session');
|
||||
session['session_id'] = this.request.cookies[self.config.cookie.name].sid;
|
||||
self._initUser(user, session);
|
||||
}
|
||||
|
||||
this.response.reply(200, {'r': 'connected'});
|
||||
}
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.send() }}} ===
|
||||
//
|
||||
// Send a message to the user specified in the query and return a
|
||||
// result declaring whether or not the message was sent. Messages
|
||||
// are only sent if the user has an active session.
|
||||
this.send = function() {
|
||||
var sent = false;
|
||||
|
||||
var user = self._session(this.request, 'object');
|
||||
var to = this.request.uri.params['to'] || '';
|
||||
|
||||
if(!user) {
|
||||
self._d('An unknown user tried to send a message to [' + to + '] without being authenticated.');
|
||||
return this.response.reply(200, {'r': 'error', 'e': 'no session found'});
|
||||
}
|
||||
|
||||
if(user.username && to &&
|
||||
to in self.users &&
|
||||
self.users[to].callback
|
||||
) {
|
||||
var time = Math.round((new Date()).getTime() / 1000);
|
||||
self.users[to].callback({
|
||||
t: 'm',
|
||||
s: user.username,
|
||||
r: to,
|
||||
m: this.request.uri.params.message
|
||||
});
|
||||
sent = true;
|
||||
}
|
||||
|
||||
self._d('User [' + user.username + '] sent a message to [' + to + '] ' + (sent ? 'successfully.' : 'UNSUCCESSFULLY.'));
|
||||
|
||||
self.users[user.username].last_active = Date.now();
|
||||
self.sessions[user.session_id].last_active = Date.now();
|
||||
this.response.reply(200, {'sent': sent});
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.status() }}} ===
|
||||
//
|
||||
// Update a user's status based on the query parameters; this includes
|
||||
// both their status code and any custom status message associated with
|
||||
// that code. If the status update is successful, send an update to the
|
||||
// user's friends.
|
||||
this.status = function() {
|
||||
var status_updated = false;
|
||||
|
||||
var user = self._session(this.request, 'object');
|
||||
|
||||
if(!user) {
|
||||
self._d('An unknown user tried to change their status without being authenticated.');
|
||||
return this.response.reply(200, {'r': 'error', 'e': 'no session found'});
|
||||
}
|
||||
|
||||
var status = this.request.uri.params.status;
|
||||
var statusMsg = status + ':' + this.request.uri.params.message;
|
||||
|
||||
user.friends.forEach(function(f) {
|
||||
if(f.u in self.users) {
|
||||
var group = null;
|
||||
for(var i=0; i < self.users[f.u]['friends'].length; i++) {
|
||||
if(self.users[f.u]['friends'][i].u == user.username) {
|
||||
self.users[f.u]['friends'][i].s = status;
|
||||
group = self.users[f.u]['friends'][i].g;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.users[f.u].callback({t: 's', s: user.username, r: f.u, m: statusMsg, g: group});
|
||||
}
|
||||
});
|
||||
|
||||
self._d('User [' + user.username + '] set his/her status to [' + statusMsg + ']. Friends notified.');
|
||||
|
||||
self.users[user.username].status = {s: status, m: this.request.uri.params.message};
|
||||
self.users[user.username].last_active = Date.now();
|
||||
self.sessions[user.session_id].last_active = Date.now();
|
||||
this.response.reply(200, {status_updated: status_updated});
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.online() }}} ===
|
||||
//
|
||||
// Return a count of the number of online users.
|
||||
this.online = function() {
|
||||
this.response.reply(200, {count: self.onlineCount});
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiLogin(username, data) }}} ===
|
||||
//
|
||||
// A Memcache API function. Initializes a new user based on the
|
||||
// sent username and decoded JSON user data.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{username}}} is the unique username to initialize.
|
||||
// * {{{data}}} is the user data outlined in the _initUser function.
|
||||
this.apiLogin = function(username, data) {
|
||||
this._initUser.call(self, username, data);
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiGetUser(username) }}} ===
|
||||
//
|
||||
// A Memcache API function. Returns user data based on a
|
||||
// specified username.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{username}}} is the name of the user to retrieve.
|
||||
this.apiGetUser = function(username) {
|
||||
var user = this.users[username];
|
||||
return {
|
||||
username: user.username,
|
||||
user_id: user.user_id,
|
||||
status: user.status,
|
||||
session_id: user.session_id
|
||||
};
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiGetSession(session_id) }}} ===
|
||||
//
|
||||
// A Memcache API function. Returns user data based on a
|
||||
// specified session id. Returns the same data as apiGetUser.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{session_id}}} is the session id of the user to retrieve.
|
||||
this.apiGetSession = function(session_id) {
|
||||
if(session_id in this.sessions)
|
||||
return this.apiGetUser(this.sessions[session_id].username);
|
||||
else
|
||||
return false;
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiGetUserList() }}} ===
|
||||
//
|
||||
// A Memcache API function. Returns user data for all online users.
|
||||
// Same data returned as apiGetUser, but in an array of all users.
|
||||
this.apiGetUserList = function() {
|
||||
var users = [];
|
||||
for(var username in this.users) {
|
||||
var user = this.users[username];
|
||||
if(user.status.s > 0) {
|
||||
users.push({
|
||||
username: user.username,
|
||||
user_id: user.user_id,
|
||||
status: user.status,
|
||||
session_id: user.session_id
|
||||
});
|
||||
}
|
||||
}
|
||||
return users;
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiOnlineCount() }}} ===
|
||||
//
|
||||
// A Memcache API function. Returns a count of all online users.
|
||||
this.apiOnlineCount = function() {
|
||||
return {count: this.onlineCount};
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiAddFriend(type, id, value) }}} ===
|
||||
//
|
||||
// A Memcache API function. Adds a user to the friends list
|
||||
// of another user. Note, however, that this only adds the friend
|
||||
// to the user's active session; it does not add the friend permanently
|
||||
// to any database that might be in use.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{type}}} is the type of identifier used to identify the user. One of
|
||||
// "session" or "username".\\
|
||||
// * {{{id}}} is the actual identifier; a username or session id.\\
|
||||
// * {{{value}}} is an array of data about the new friend:
|
||||
// ** {{{u}}} is the username of the friend.
|
||||
// ** {{{g}}} is the group into which the friend will be placed.
|
||||
this.apiAddFriend = function(type, id, value) {
|
||||
if(type == 'session') {
|
||||
if(id in this.sessions)
|
||||
id = this.sessions[id].username;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if(id in this.users && typeof value == 'object' && 'u' in value) {
|
||||
if(value.u in this.users) {
|
||||
value['s'] = this.users[value.u].status.s;
|
||||
|
||||
var status = this.users[value.u].status.s + ':' +
|
||||
this.users[value.u].status.m;
|
||||
|
||||
this.users[id].callback({t: 's', s: value.u, r: id, m: status, g: value.g});
|
||||
}
|
||||
|
||||
this.users[id].friends.push(value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiRemoveFriend(type, id, value) }}} ===
|
||||
//
|
||||
// A Memcache API function. Does the exact opposite of {{{apiAddFriend}}}.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{type}}} is the type of identifier used to identify the user. One of
|
||||
// "session" or "username".\\
|
||||
// * {{{id}}} is the actual identifier; a username or session id.\\
|
||||
// * {{{value}}} is the username of the friend to remove.
|
||||
this.apiRemoveFriend = function(type, id, value) {
|
||||
if(type == 'session') {
|
||||
if(id in this.sessions)
|
||||
id = this.sessions[id].username;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if(id in this.users && typeof value == 'string' && value.length > 0) {
|
||||
for(var i = 0; i < this.users[id].friends.length; i++) {
|
||||
if(this.users[id].friends[i].u == value) {
|
||||
var group = this.users[id].friends[i].g;
|
||||
// should be a 'remove' type, not a 'status' type...we'll fix this later.
|
||||
this.users[id].callback({t: 's', s: value, r: id, m: '0:', g: group});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiBroadcastMessage(type, id, value) }}} ===
|
||||
//
|
||||
// A Memcache API function. Broadcasts a message of type "message" ("m")
|
||||
// to all signed-in users.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{type}}} should be "from".\\
|
||||
// * {{{id}}} is the name of the sender; can be anything.\\
|
||||
// * {{{value}}} is message.
|
||||
this.apiBroadcastMessage = function(type, id, value) {
|
||||
if(type != 'from' || !id.length)
|
||||
return false;
|
||||
|
||||
for(user in this.users) {
|
||||
this.users[user].callback({t: 'm', s: id, r: user, m: value});
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// === {{{ AjaxIM.apiBroadcastRaw(type, id, value) }}} ===
|
||||
//
|
||||
// A Memcache API function. Broadcasts a message of the specified
|
||||
// type to all signed-in users.
|
||||
//
|
||||
// ==== Parameters ====
|
||||
// * {{{type}}} should be "from".\\
|
||||
// * {{{id}}} is the name of the sender; can be anything.\\
|
||||
// * {{{value}}} is message object:
|
||||
// ** {{{t}}} is the type of message.
|
||||
// ** {{{s}}} is the sender.
|
||||
// ** {{{r}}} is the recipient (usually irrelevant in broadcasted messages).
|
||||
// ** {{{m}}} is the content of the message.
|
||||
this.apiBroadcastRaw = function(type, id, value) {
|
||||
// Why "from?" This needs to be reconsidered.
|
||||
if(type != 'from' || !id.length || typeof value != 'object')
|
||||
return false;
|
||||
|
||||
for(user in this.users) {
|
||||
this.users[user].callback(value);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
// == WebServer Class ==
|
||||
function WebServer(host, port) {
|
||||
var self = this;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
|
||||
this.urlMap = [];
|
||||
this.get = function(path, handler) {
|
||||
this._map(path, 'GET', handler);
|
||||
};
|
||||
|
||||
this.post = function(path, handler) {
|
||||
this._map(path, 'POST', handler);
|
||||
};
|
||||
|
||||
this._map = function(path, type, handler) {
|
||||
var path_regex = new RegExp(path);
|
||||
this.urlMap.push([path_regex, type, handler]);
|
||||
};
|
||||
|
||||
this._notFound = function() {
|
||||
var nf = 'Not Found\n';
|
||||
this.response.sendHeader(404, {
|
||||
'Content-Type': 'text/plain',
|
||||
'Content-Length': nf.length
|
||||
});
|
||||
this.response.sendBody(nf);
|
||||
this.response.finish();
|
||||
};
|
||||
|
||||
this._parseCookies = function(str) {
|
||||
var c = {};
|
||||
if(!str) return c;
|
||||
|
||||
str = str.replace(/;/g, ', ');
|
||||
str.split(', ').forEach(function(cookie) {
|
||||
var parts = cookie.split('='),
|
||||
name = parts[0].trim(),
|
||||
value = parts[1].trim();
|
||||
|
||||
try {
|
||||
c[name] = JSON.parse(decodeURIComponent(value));
|
||||
} catch(e) {
|
||||
c[name] = value;
|
||||
}
|
||||
});
|
||||
return c;
|
||||
}
|
||||
|
||||
this.start = function() {
|
||||
http.createServer(function(request, response) {
|
||||
var handler = self._notFound, args = [];
|
||||
|
||||
if(typeof request['uri'] == 'undefined') {
|
||||
uri = url.parse(request.url, true);
|
||||
request['uri'] = {
|
||||
'path': uri.pathname,
|
||||
'params': uri.query || {}
|
||||
};
|
||||
}
|
||||
|
||||
for(var i = 0; i < self.urlMap.length; i++) {
|
||||
var map = self.urlMap[i];
|
||||
if(map[1] == request.method && map[0].test(request.uri.path)) {
|
||||
args = map[0].exec(request.uri.path);
|
||||
handler = map[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
request.setBodyEncoding('utf8');
|
||||
|
||||
request.cookies = self._parseCookies(request.headers['cookie']);
|
||||
|
||||
response.reply = function(code, obj) {
|
||||
var content = JSON.stringify(obj);
|
||||
content = request.uri.params.callback + '(' + content + ');';
|
||||
|
||||
response.sendHeader(code, {
|
||||
'content-type': 'text/html',
|
||||
'content-length': content.length,
|
||||
'expires': 'Mon, 26 Jul 1997 05:00:00 GMT',
|
||||
'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0',
|
||||
'pragma': 'no-cache'
|
||||
});
|
||||
response.sendBody(content);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
handler.apply({request: request, response: response}, args.slice(1));
|
||||
}).listen(this.port, this.host);
|
||||
};
|
||||
}
|
||||
|
||||
// == MemcacheServer Class ==
|
||||
function MemcacheServer(host, port) {
|
||||
var self = this;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
|
||||
this._cb_login = null;
|
||||
this._cb_logout = null;
|
||||
this._cb_getuser = null;
|
||||
this._cb_custom = {};
|
||||
|
||||
this.login = function(callback) {
|
||||
this._cb_login = callback;
|
||||
};
|
||||
|
||||
this.logout = function(callback) {
|
||||
this._cb_logout = callback;
|
||||
};
|
||||
|
||||
this.getUser = function(callback) {
|
||||
this._cb_getuser = callback;
|
||||
};
|
||||
|
||||
this.setCustom = function(action, callback) {
|
||||
this._cb_custom[action] = callback;
|
||||
};
|
||||
|
||||
this._add = function(username, data) {
|
||||
if(typeof self._cb_login != 'function' ||
|
||||
!('user_id' in data) || !('session_id' in data)) {
|
||||
this.send('NOT_STORED\r\n');
|
||||
} else if(self._cb_login(username, data)) {
|
||||
this.send('STORED\r\n');
|
||||
} else {
|
||||
this.send('NOT_STORED\r\n');
|
||||
}
|
||||
};
|
||||
|
||||
this._set = function(username, action, value) {
|
||||
if(!action.length || !value) {
|
||||
this.send('NOT_STORED\r\n');
|
||||
} else {
|
||||
var result = false;
|
||||
for(var axn in self._cb_custom) {
|
||||
var rx = new RegExp(axn);
|
||||
if(rx.test(action)) {
|
||||
result = self._cb_custom[axn](username[0], username[1], value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(result) {
|
||||
this.send('STORED\r\n');
|
||||
} else {
|
||||
this.send('NOT_STORED\r\n');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._delete = function(username) {
|
||||
if(typeof self._cb_logout == 'function' && self._cb_logout(username)) {
|
||||
this.send('DELETED\r\n');
|
||||
} else {
|
||||
this.send('NOT_FOUND\r\n');
|
||||
}
|
||||
};
|
||||
|
||||
this._get = function(username) {
|
||||
if(typeof self._cb_getuser == 'function') {
|
||||
var user = self._cb_getuser(username);
|
||||
if(user) {
|
||||
var json_user = JSON.stringify(user);
|
||||
this.send('VALUE ' + username + ' 0 ' + json_user.length + '\r\n')
|
||||
this.send(json_user + '\r\n');
|
||||
}
|
||||
}
|
||||
|
||||
this.send('END\r\n');
|
||||
};
|
||||
|
||||
this._gets = function(list) {
|
||||
if(typeof self._cb_getuser == 'function') {
|
||||
var results = [];
|
||||
for(var i = 0; i < list.length; i++) {
|
||||
var user = self._cb_getuser(list[i]);
|
||||
if(user) {
|
||||
var json_user = JSON.stringify(user);
|
||||
results.push('VALUE ' + list[i] + ' 0 ' + json_user.length + '\r\n');
|
||||
results.push(json_user + '\r\n');
|
||||
}
|
||||
}
|
||||
|
||||
if(results.length) {
|
||||
this.send(results.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
this.send('END\r\n');
|
||||
};
|
||||
|
||||
this.start = function() {
|
||||
tcp.createServer(function(socket) {
|
||||
socket.setEncoding('utf8');
|
||||
|
||||
var cmd = {};
|
||||
var incoming = '';
|
||||
socket.addListener('receive', function(data) {
|
||||
incoming += data;
|
||||
|
||||
// We're in the middle of receiving some data, so we can't
|
||||
// parse it yet.
|
||||
if(data.substring(data.length - 2) != '\r\n')
|
||||
return;
|
||||
|
||||
data = incoming;
|
||||
incoming = '';
|
||||
|
||||
var parts = data.split(/ /);
|
||||
if(parts[0].match(/^(add|set|delete|get|gets)$/i)) {
|
||||
switch(parts[0]) {
|
||||
case 'add':
|
||||
var message = JSON.parse(data.split('\r\n')[1]);
|
||||
self._add.call(socket, parts[1].replace('\r\n', ''), message);
|
||||
break;
|
||||
|
||||
case 'set':
|
||||
var message = data.split('\r\n')[1];
|
||||
try {
|
||||
message = JSON.parse(message);
|
||||
} catch(e) {}
|
||||
var subparts = parts[1].replace('\r\n', '').split('/');
|
||||
self._set.call(socket, subparts.slice(0, 2), subparts.slice(2).join('/'), message);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
self._delete.call(socket, parts[1].replace('\r\n', ''));
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
self._get.call(socket, parts[1].replace('\r\n', ''));
|
||||
break;
|
||||
|
||||
case 'gets':
|
||||
var getsparts = parts.slice(1);
|
||||
getsparts[getsparts.length - 1] =
|
||||
getsparts[getsparts.length - 1].replace('\r\n', '');
|
||||
self._gets.call(socket, getsparts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}).listen(this.port, this.host);
|
||||
};
|
||||
}
|
||||
|
||||
var im = new AjaxIM(config);
|
||||
im.init();
|
||||
@@ -0,0 +1,90 @@
|
||||
var crypto = require('crypto');
|
||||
|
||||
var Session = function(username) {
|
||||
this.username = username;
|
||||
this.authenticated = false;
|
||||
this.lastAccess = new Date().getTime();
|
||||
this.client = -1;
|
||||
this.identifier = -1;
|
||||
this.friends = [];
|
||||
this.status = 'online';
|
||||
};
|
||||
|
||||
var MemorySessionStore = function() {
|
||||
this.sessions = {};
|
||||
this.sessionClientMap = {};
|
||||
this.sessionIdentifierMap = {};
|
||||
};
|
||||
|
||||
MemorySessionStore.prototype.create = function(username, client, friends) {
|
||||
var session = new Session(username);
|
||||
session.client = client;
|
||||
|
||||
if(friends) {
|
||||
for(var i = 0, fl = friends.length; i < fl; i++)
|
||||
session.friends.push(friends[i])
|
||||
}
|
||||
|
||||
this.sessions[username] = session;
|
||||
this.sessionClientMap[client.sessionId] = username;
|
||||
|
||||
// create a re-authentication identifier
|
||||
var md5 = crypto.createHash('md5'),
|
||||
identifier = md5.update(username + client.sessionId).digest('hex');
|
||||
this.sessionIdentifierMap[identifier] = username;
|
||||
session.identifier = identifier;
|
||||
|
||||
return identifier;
|
||||
};
|
||||
|
||||
MemorySessionStore.prototype.set = function(username, session) {
|
||||
this.sessions[username] = session;
|
||||
this.sessionClientMap[session.client.sessionId] = username;
|
||||
this.sessionIdentifierMap[session.identifier] = username;
|
||||
};
|
||||
|
||||
MemorySessionStore.prototype.touch = function(session, client) {
|
||||
var new_session = session;
|
||||
new_session.lastAccess = new Date().getTime();
|
||||
new_session.client = client;
|
||||
|
||||
this.remove(new_session.username);
|
||||
this.set(new_session.username, new_session);
|
||||
};
|
||||
|
||||
MemorySessionStore.prototype.get = function(key, val) {
|
||||
switch(key) {
|
||||
case 'client':
|
||||
return this.sessions[this.sessionClientMap[val]];
|
||||
break;
|
||||
|
||||
case 'username':
|
||||
return this.sessions[val];
|
||||
break;
|
||||
|
||||
case 'identifier':
|
||||
return this.sessions[this.sessionIdentifierMap[val]];
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
MemorySessionStore.prototype.all = function() {
|
||||
return this.sessions;
|
||||
};
|
||||
|
||||
MemorySessionStore.prototype.remove = function(username) {
|
||||
if(this.sessions[username]) {
|
||||
var clientid = this.sessions[username].client.sessionId,
|
||||
identifier = this.sessions[username].identifier;
|
||||
delete this.sessionClientMap[clientid];
|
||||
delete this.sessionIdentifierMap[identifier];
|
||||
delete this.sessions[username];
|
||||
}
|
||||
};
|
||||
|
||||
var instance = new MemorySessionStore();
|
||||
module.exports = function getInstance() {
|
||||
return instance;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// == Server Configuration ==
|
||||
//
|
||||
// This is the configuration file for the Node.js Ajax IM server. Here, you
|
||||
// can set which ports will be used for the public and internal servers,
|
||||
// as well as other settings such as the session cookie name and expiration.
|
||||
|
||||
// === Host and Port ===
|
||||
//
|
||||
// Define the host and port that Ajax IM will run on.
|
||||
//
|
||||
// Note: Setting APP_HOST to null will run the server on port 8000 for any
|
||||
// hostname!
|
||||
APP_HOST = 'localhost';
|
||||
APP_PORT = 8000;
|
||||
|
||||
// Document me!
|
||||
AUTH_LIBRARY = 'example';
|
||||
SESSION_STORE = 'memory';
|
||||
MESSAGE_HANDLER = 'default';
|
||||
|
||||
// Document me!
|
||||
SESSION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
// === Daemon ===
|
||||
//
|
||||
// Define where the PID and log files will be deposited when run as a daemon.
|
||||
/*
|
||||
// Broken! Removed for the time being.
|
||||
PID_FILE = '/tmp/ajaxim.pid';
|
||||
LOG_FILE = '/var/run/ajaxim.log';
|
||||
*/
|
||||
|
Antes Largura: | Altura: | Tamanho: 121 B |
|
Antes Largura: | Altura: | Tamanho: 176 B |
|
Antes Largura: | Altura: | Tamanho: 552 B |
|
Antes Largura: | Altura: | Tamanho: 212 B |
|
Antes Largura: | Altura: | Tamanho: 706 B |
|
Antes Largura: | Altura: | Tamanho: 142 B |
|
Antes Largura: | Altura: | Tamanho: 141 B |
|
Antes Largura: | Altura: | Tamanho: 234 B |
|
Antes Largura: | Altura: | Tamanho: 109 B |
@@ -1,132 +0,0 @@
|
||||
<!-- Ajax IM Instant Messenger, Footer Bar -->
|
||||
<ul id="imjs-bar">
|
||||
<li id="imjs-friends" class="imjs-not-connected">
|
||||
<span class="imjs-tab-text"><strong>•</strong> Friends <span>(<span>0</span>)</span></span>
|
||||
<div id="imjs-friends-panel" class="imjs-chatbox">
|
||||
<div class="imjs-header">
|
||||
<span>{username}</span>
|
||||
<a href="#" class="imjs-minimize">_</a>
|
||||
</div>
|
||||
<ul id="imjs-friends-list">
|
||||
<li class="imjs-friend-group imjs-default">
|
||||
<span class="imjs-friend-group-header">{group}</span>
|
||||
<ul>
|
||||
<li class="imjs-friend imjs-default"><strong>•</strong> {username}</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<p><input type="text" id="imjs-search-friends" value="Search" /></p>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
<li id="imjs-scroll-right" class="imjs-scroll">{count}</li>
|
||||
<li class="imjs-tab imjs-default">
|
||||
<span class="imjs-tab-text"><strong>•</strong> {label} <a href="#" class="imjs-close">x</a></span>
|
||||
<span class="imjs-notification">{count}</span>
|
||||
<form class="imjs-chatbox">
|
||||
<div>
|
||||
<div class="imjs-header">
|
||||
<span>{username}</span>
|
||||
<a href="#" class="imjs-close">x</a>
|
||||
<a href="#" class="imjs-minimize">_</a>
|
||||
</div>
|
||||
<ul class="imjs-msglog">
|
||||
<li class="imjs-date">
|
||||
<ul>
|
||||
<li>
|
||||
<span class="imjs-msg-time">hh:MM TT •</span>
|
||||
<p class="imjs-date-date">ddd, mmmm d, yyyy</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="imjs-error">
|
||||
<ul>
|
||||
<li>
|
||||
<span class="imjs-msg-time">hh:MM TT •</span>
|
||||
<p class="imjs-error-error">ddd, mmmm d, yyyy</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="imjs-msg-a">
|
||||
<span>{username}</span>
|
||||
<ul class="imjs-msg-a-container">
|
||||
<li class="imjs-msg-a-msg">
|
||||
<span class="imjs-msg-time">hh:MM TT •</span>
|
||||
<p>{message}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="imjs-msg-b">
|
||||
<span>{username}</span>
|
||||
<ul class="imjs-msg-b-container">
|
||||
<li class="imjs-msg-b-msg">
|
||||
<span class="imjs-msg-time">hh:MM TT •</span>
|
||||
<p>{message}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<textarea class="imjs-input"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
<li id="imjs-scroll-left" class="imjs-scroll">{count}</li>
|
||||
</ul>
|
||||
<span class="imjs-tooltip"><p>{tip}</p></span>
|
||||
|
||||
<!-- Ajax IM Instant Messenger, Login/Registration Form -->
|
||||
<div id="imjs-lr">
|
||||
<form id="imjs-login">
|
||||
<fieldset>
|
||||
<h2>Login</h2>
|
||||
|
||||
<p class="error"></p>
|
||||
|
||||
<p>
|
||||
<label for="imjs-login-username">Username</label>
|
||||
<input type="text" name="username" id="imjs-login-username" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="imjs-login-password">Password</label>
|
||||
<input type="password" name="password" id="imjs-login-password" />
|
||||
</p>
|
||||
|
||||
<p class="imjs-submit">
|
||||
<button id="imjs-login-submit">Sign In</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<p id="imjs-logged-in">Signed in as <strong>{username}</strong>. <a href="#logout">Sign Out</a>.</p>
|
||||
</form>
|
||||
|
||||
<form id="imjs-register">
|
||||
<fieldset>
|
||||
<h2>Register</h2>
|
||||
|
||||
<p class="error"></p>
|
||||
|
||||
<p>
|
||||
<label for="imjs-register-username">Username</label>
|
||||
<input type="text" name="username" id="imjs-register-username" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="imjs-register-password">Password</label>
|
||||
<input type="password" name="password" id="imjs-register-password" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="imjs-register-cpassword">Confirm Password</label>
|
||||
<input type="password" name="cpassword" id="imjs-register-cpassword" />
|
||||
</p>
|
||||
|
||||
<p class="imjs-submit">
|
||||
<button id="imjs-register-submit">Register</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||