Merge branch 'master' into gh-pages

Conflicts:
	build.js
	converse.min.css
	converse.min.js
	docs/doctrees/environment.pickle
	docs/html/searchindex.js
	index.html
This commit is contained in:
JC Brand 2013-07-30 23:40:59 +02:00
commit 5f97f7ea28
59 changed files with 1186 additions and 26959 deletions

3
.bowerrc Normal file
View File

@ -0,0 +1,3 @@
{
"directory": "components"
}

4
.travis.yml Normal file
View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- "0.10.13"
script: grunt test

View File

@ -1,8 +1,8 @@
Changelog
=========
0.5 (Unreleased)
----------------
0.5.0 (2013-07-30)
------------------
- #09 Remove dependency on AMD/require.js [jcbrand]
- #22 Fixed compare operator in strophe.muc [sonata82]
@ -11,9 +11,10 @@ Changelog
- #25 Using span with css instead of img [matheus-morfi]
- #26 Only the first minute digit shown in chatbox. [jcbrand]
- #28 Add Brazilian Portuguese translations [matheus-morfi]
- Use Bower to manage 3rd party dependencies. [jcbrand]
0.4 (2013-06-03)
----------------
0.4.0 (2013-06-03)
------------------
- CSS tweaks: fixed overflowing text in status message and chatrooms list. [jcbrand]
- Bugfix: Couldn't join chatroom when clicking from a list of rooms. [jcbrand]
@ -24,8 +25,8 @@ Changelog
- Reconnect automatically when the connection drops. [jcbrand]
- Add support for internationalization. [jcbrand]
0.3 (2013-05-21)
----------------
0.3.0 (2013-05-21)
------------------
- Add vCard support [jcbrand]
- Remember custom status messages upon reload. [jcbrand]
@ -40,15 +41,15 @@ Changelog
- Multi-user chatrooms are now configurable. [jcbrand]
0.2 (2013-03-28)
----------------
0.2.0 (2013-03-28)
------------------
- Performance enhancements and general script cleanup [ichim-david]
- Add "Connecting to chat..." info [alecghica]
- Various smaller improvements and bugfixes [jcbrand]
0.1 (2012-06-12)
----------------
0.1.0 (2012-06-12)
------------------
- Created [jcbrand]

View File

@ -2,7 +2,7 @@
Contributing to Converse.js
===========================
Thanks for contributing to Converse.js_!
Thanks for contributing to Converse.js_.
Please follow the usual github workflow. Create your own local fork of this repository,
make your changes and then submit a pull request.
@ -19,12 +19,27 @@ for testing.
Take a look at ``tests.html`` and ``spec/MainSpec.js`` to see how
the tests are implemented.
Check that the tests run
------------------------
If you are unsure how to write tests, please `contact me`_ and I'll be happy to
help.
Check that the Jasmine BDD tests complete sucessfully. Open tests.html in your
Check that the tests pass
-------------------------
Check that the Jasmine tests complete sucessfully. Open tests.html in your
browser, and the tests will run automatically.
You can see the current test output online, here: http://conversejs.org/tests.html
On the command line you can run ``grunt test`` (if you have before run ``npm
install``).
Check your code for errors or bad habits by running JSHint
----------------------------------------------------------
If you haven't yet done so, run ``npm install`` to install all development
dependencies.
Then run ``grunt jshint`` and check the output.
.. _Converse.js: http://conversejs.org
.. _`contact me`: http://opkode.com/contact.html

View File

@ -1,20 +1,115 @@
module.exports = function(grunt) {
var cfg = require('./package.json');
grunt.initConfig({
jshint: {
options: {
trailing: true
options: {
trailing: true
},
target: {
src : [
'converse.js',
'mock.js',
'main.js',
'tests_main.js',
'spec/*.js'
]
}
},
target: {
src : [
'converse.js',
'mock.js',
'main.js',
'tests_main.js',
'spec/*.js'
]
}
cssmin: {
options: {
banner: "/*"+
"* Converse.js (Web-based XMPP instant messaging client) \n"+
"* http://conversejs.org \n"+
"* Copyright (c) 2012, Jan-Carel Brand <jc@opkode.com> \n"+
"* Dual licensed under the MIT and GPL Licenses \n"+
"*/"
},
minify: {
dest: 'converse-'+cfg.version+'.min.css',
src: ['converse.css']
}
},
requirejs: {
compile: {
options: {
baseUrl: ".",
name: "main",
out: "converse-"+cfg.version+".min.js",
paths: {
"require": "components/requirejs/require",
"jquery": "components/jquery/jquery",
"jed": "components/jed/jed",
"locales": "locale/locales",
"af": "locale/af/LC_MESSAGES/af",
"en": "locale/en/LC_MESSAGES/en",
"de": "locale/de/LC_MESSAGES/de",
"es": "locale/es/LC_MESSAGES/es",
"it": "locale/it/LC_MESSAGES/it",
"pt_BR": "locale/pt_BR/LC_MESSAGES/pt_BR",
"sjcl": "components/sjcl/sjcl",
"tinysort": "components/tinysort/src/jquery.tinysort",
"underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone",
"localstorage": "components/backbone.localStorage/backbone.localStorage",
"strophe": "components/strophe/strophe",
"strophe.muc": "components/strophe.muc/index",
"strophe.roster": "components/strophe.roster/index",
"strophe.vcard": "components/strophe.vcard/index",
"strophe.disco": "components/strophe.disco/index"
},
done: function(done, output) {
var duplicates = require('rjs-build-analysis').duplicates(output);
if (duplicates.length > 0) {
grunt.log.subhead('Duplicates found in requirejs build:');
grunt.log.warn(duplicates);
done(new Error('r.js built duplicate modules, please check the excludes option.'));
}
done();
}
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['jshint']);
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.registerTask('test', 'Run Tests', function () {
var done = this.async();
var child_process = require('child_process');
var exec = child_process.exec;
exec('./node_modules/.bin/phantomjs '+
'node_modules/jasmine-reporters/test/phantomjs-testrunner.js '+
__dirname+'/tests.html',
function (err, stdout, stderr) {
if (err) {
grunt.log.write('Tests failed with error code '+err.code);
grunt.log.write(stderr);
}
grunt.log.write(stdout);
done();
});
});
grunt.registerTask('fetch', 'Set up the development environment', function () {
var done = this.async();
var child_process = require('child_process');
var exec = child_process.exec;
exec('bower update && cd ./components/strophe && make normal',
function (err, stdout, stderr) {
if (err) {
grunt.log.write('build failed with error code '+err.code);
grunt.log.write(stderr);
}
grunt.log.write(stdout);
done();
});
});
// TODO: update CHANGES.txt with release date
grunt.registerTask('release', 'Create a new release', ['requirejs', 'cssmin']);
grunt.registerTask('check', 'Perform all checks (e.g. before releasing)', function () {
grunt.task.run('jshint', 'test');
});
};

File diff suppressed because it is too large Load Diff

View File

@ -1,192 +0,0 @@
/**
* Backbone localStorage Adapter
* Version 1.1.0
*
* https://github.com/jeromegn/Backbone.localStorage
*/
(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(["underscore","backbone"], function(_, Backbone) {
// Use global variables if the locals is undefined.
return factory(_ || root._, Backbone || root.Backbone);
});
} else {
// RequireJS isn't being used. Assume underscore and backbone is loaded in <script> tags
factory(_, Backbone);
}
}(this, function(_, Backbone) {
// A simple module to replace `Backbone.sync` with *localStorage*-based
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
// as that.
// Hold reference to Underscore.js and Backbone.js in the closure in order
// to make things work even if they are removed from the global namespace
// Generate four random hex digits.
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
// Generate a pseudo-GUID by concatenating random hexadecimal.
function guid() {
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
};
// Our Store is represented by a single JS object in *localStorage*. Create it
// with a meaningful name, like the name you'd give a table.
// window.Store is deprectated, use Backbone.LocalStorage instead
Backbone.LocalStorage = window.Store = function(name) {
this.name = name;
var store = this.localStorage().getItem(this.name);
this.records = (store && store.split(",")) || [];
};
_.extend(Backbone.LocalStorage.prototype, {
// Save the current state of the **Store** to *localStorage*.
save: function() {
this.localStorage().setItem(this.name, this.records.join(","));
},
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
// have an id of it's own.
create: function(model) {
if (!model.id) {
model.id = guid();
model.set(model.idAttribute, model.id);
}
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model));
this.records.push(model.id.toString());
this.save();
return this.find(model);
},
// Update a model by replacing its copy in `this.data`.
update: function(model) {
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model));
if (!_.include(this.records, model.id.toString()))
this.records.push(model.id.toString()); this.save();
return this.find(model);
},
// Retrieve a model from `this.data` by id.
find: function(model) {
return this.jsonData(this.localStorage().getItem(this.name+"-"+model.id));
},
// Return the array of all models currently in storage.
findAll: function() {
return _(this.records).chain()
.map(function(id){
return this.jsonData(this.localStorage().getItem(this.name+"-"+id));
}, this)
.compact()
.value();
},
// Delete a model from `this.data`, returning it.
destroy: function(model) {
if (model.isNew())
return false
this.localStorage().removeItem(this.name+"-"+model.id);
this.records = _.reject(this.records, function(id){
return id === model.id.toString();
});
this.save();
return model;
},
localStorage: function() {
return localStorage;
},
// fix for "illegal access" error on Android when JSON.parse is passed null
jsonData: function (data) {
return data && JSON.parse(data);
}
});
// localSync delegate to the model or collection's
// *localStorage* property, which should be an instance of `Store`.
// window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead
Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) {
var store = model.localStorage || model.collection.localStorage;
var resp, errorMessage, syncDfd = $.Deferred && $.Deferred(); //If $ is having Deferred - use it.
try {
switch (method) {
case "read":
resp = model.id != undefined ? store.find(model) : store.findAll();
break;
case "create":
resp = store.create(model);
break;
case "update":
resp = store.update(model);
break;
case "delete":
resp = store.destroy(model);
break;
}
} catch(error) {
if (error.code === DOMException.QUOTA_EXCEEDED_ERR && window.localStorage.length === 0)
errorMessage = "Private browsing is unsupported";
else
errorMessage = error.message;
}
if (resp) {
if (options && options.success)
if (Backbone.VERSION === "0.9.10") {
options.success(model, resp, options);
} else {
options.success(resp);
}
if (syncDfd)
syncDfd.resolve(resp);
} else {
errorMessage = errorMessage ? errorMessage
: "Record Not Found";
if (options && options.error)
if (Backbone.VERSION === "0.9.10") {
options.error(model, errorMessage, options);
} else {
options.error(errorMessage);
}
if (syncDfd)
syncDfd.reject(errorMessage);
}
// add compatibility with $.ajax
// always execute callback for success and error
if (options && options.complete) options.complete(resp);
return syncDfd && syncDfd.promise();
};
Backbone.ajaxSync = Backbone.sync;
Backbone.getSyncMethod = function(model) {
if(model.localStorage || (model.collection && model.collection.localStorage)) {
return Backbone.localSync;
}
return Backbone.ajaxSync;
};
// Override 'Backbone.sync' to default to localSync,
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
Backbone.sync = function(method, model, options) {
return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
};
return Backbone.LocalStorage;
}));

View File

@ -1,20 +0,0 @@
Copyright (c) 2008-2011 Pivotal Labs
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.

View File

@ -1,681 +0,0 @@
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
var results = child.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
return status;
};
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
var parentDiv = this.dom.summary;
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
var parent = child[parentSuite];
if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
}
parentDiv = this.views.suites[parent.id].element;
}
parentDiv.appendChild(childElement);
};
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
for(var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
}
};
jasmine.HtmlReporter = function(_doc) {
var self = this;
var doc = _doc || window.document;
var reporterView;
var dom = {};
// Jasmine Reporter Public Interface
self.logRunningSpecs = false;
self.reportRunnerStarting = function(runner) {
var specs = runner.specs() || [];
if (specs.length == 0) {
return;
}
createReporterDom(runner.env.versionString());
doc.body.appendChild(dom.reporter);
setExceptionHandling();
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView.addSpecs(specs, self.specFilter);
};
self.reportRunnerResults = function(runner) {
reporterView && reporterView.complete();
};
self.reportSuiteResults = function(suite) {
reporterView.suiteComplete(suite);
};
self.reportSpecStarting = function(spec) {
if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
self.reportSpecResults = function(spec) {
reporterView.specComplete(spec);
};
self.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
self.specFilter = function(spec) {
if (!focusedSpecName()) {
return true;
}
return spec.getFullName().indexOf(focusedSpecName()) === 0;
};
return self;
function focusedSpecName() {
var specName;
(function memoizeFocusedSpec() {
if (specName) {
return;
}
var paramMap = [];
var params = jasmine.HtmlReporter.parameters(doc);
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
specName = paramMap.spec;
})();
return specName;
}
function createReporterDom(version) {
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
dom.banner = self.createDom('div', { className: 'banner' },
self.createDom('span', { className: 'title' }, "Jasmine "),
self.createDom('span', { className: 'version' }, version)),
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
dom.alert = self.createDom('div', {className: 'alert'},
self.createDom('span', { className: 'exceptions' },
self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
dom.results = self.createDom('div', {className: 'results'},
dom.summary = self.createDom('div', { className: 'summary' }),
dom.details = self.createDom('div', { id: 'details' }))
);
}
function noTryCatch() {
return window.location.search.match(/catch=false/);
}
function searchWithCatch() {
var params = jasmine.HtmlReporter.parameters(window.document);
var removed = false;
var i = 0;
while (!removed && i < params.length) {
if (params[i].match(/catch=/)) {
params.splice(i, 1);
removed = true;
}
i++;
}
if (jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
return params.join("&");
}
function setExceptionHandling() {
var chxCatch = document.getElementById('no_try_catch');
if (noTryCatch()) {
chxCatch.setAttribute('checked', true);
jasmine.CATCH_EXCEPTIONS = false;
}
chxCatch.onclick = function() {
window.location.search = searchWithCatch();
};
}
};
jasmine.HtmlReporter.parameters = function(doc) {
var paramStr = doc.location.search.substring(1);
var params = [];
if (paramStr.length > 0) {
params = paramStr.split('&');
}
return params;
}
jasmine.HtmlReporter.sectionLink = function(sectionName) {
var link = '?';
var params = [];
if (sectionName) {
params.push('spec=' + encodeURIComponent(sectionName));
}
if (!jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
if (params.length > 0) {
link += params.join("&");
}
return link;
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
jasmine.HtmlReporter.ReporterView = function(dom) {
this.startedAt = new Date();
this.runningSpecCount = 0;
this.completeSpecCount = 0;
this.passedCount = 0;
this.failedCount = 0;
this.skippedCount = 0;
this.createResultsMenu = function() {
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
' | ',
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
this.summaryMenuItem.onclick = function() {
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
};
this.detailsMenuItem.onclick = function() {
showDetails();
};
};
this.addSpecs = function(specs, specFilter) {
this.totalSpecCount = specs.length;
this.views = {
specs: {},
suites: {}
};
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
if (specFilter(spec)) {
this.runningSpecCount++;
}
}
};
this.specComplete = function(spec) {
this.completeSpecCount++;
if (isUndefined(this.views.specs[spec.id])) {
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
}
var specView = this.views.specs[spec.id];
switch (specView.status()) {
case 'passed':
this.passedCount++;
break;
case 'failed':
this.failedCount++;
break;
case 'skipped':
this.skippedCount++;
break;
}
specView.refresh();
this.refresh();
};
this.suiteComplete = function(suite) {
var suiteView = this.views.suites[suite.id];
if (isUndefined(suiteView)) {
return;
}
suiteView.refresh();
};
this.refresh = function() {
if (isUndefined(this.resultsMenu)) {
this.createResultsMenu();
}
// currently running UI
if (isUndefined(this.runningAlert)) {
this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
dom.alert.appendChild(this.runningAlert);
}
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
// skipped specs UI
if (isUndefined(this.skippedAlert)) {
this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
}
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.skippedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.skippedAlert);
}
// passing specs UI
if (isUndefined(this.passedAlert)) {
this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
}
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
// failing specs UI
if (isUndefined(this.failedAlert)) {
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
}
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
if (this.failedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.failedAlert);
dom.alert.appendChild(this.resultsMenu);
}
// summary info
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
};
this.complete = function() {
dom.alert.removeChild(this.runningAlert);
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.failedCount === 0) {
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
} else {
showDetails();
}
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
};
return this;
function showDetails() {
if (dom.reporter.className.search(/showDetails/) === -1) {
dom.reporter.className += " showDetails";
}
}
function isUndefined(obj) {
return typeof obj === 'undefined';
}
function isDefined(obj) {
return !isUndefined(obj);
}
function specPluralizedFor(count) {
var str = count + " spec";
if (count > 1) {
str += "s"
}
return str;
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.spec = spec;
this.dom = dom;
this.views = views;
this.symbol = this.createDom('li', { className: 'pending' });
this.dom.symbolSummary.appendChild(this.symbol);
this.summary = this.createDom('div', { className: 'specSummary' },
this.createDom('a', {
className: 'description',
href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.description)
);
this.detail = this.createDom('div', { className: 'specDetail' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.getFullName())
);
};
jasmine.HtmlReporter.SpecView.prototype.status = function() {
return this.getSpecStatus(this.spec);
};
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
this.symbol.className = this.status();
switch (this.status()) {
case 'skipped':
break;
case 'passed':
this.appendSummaryToSuiteDiv();
break;
case 'failed':
this.appendSummaryToSuiteDiv();
this.appendFailureDetail();
break;
}
};
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
this.summary.className += ' ' + this.status();
this.appendToSummary(this.spec, this.summary);
};
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
this.detail.className += ' ' + this.status();
var resultItems = this.spec.results().getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
this.detail.appendChild(messagesDiv);
this.dom.details.appendChild(this.detail);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
this.suite = suite;
this.dom = dom;
this.views = views;
this.element = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
);
this.appendToSummary(this.suite, this.element);
};
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
return this.getSpecStatus(this.suite);
};
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
this.element.className += " " + this.status();
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
/* @deprecated Use jasmine.HtmlReporter instead
*/
jasmine.TrivialReporter = function(doc) {
this.document = doc || document;
this.suiteDivs = {};
this.logRunningSpecs = false;
};
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) { el.appendChild(child); }
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
var showPassed, showSkipped;
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
this.createDom('div', { className: 'banner' },
this.createDom('div', { className: 'logo' },
this.createDom('span', { className: 'title' }, "Jasmine"),
this.createDom('span', { className: 'version' }, runner.env.versionString())),
this.createDom('div', { className: 'options' },
"Show ",
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
)
),
this.runnerDiv = this.createDom('div', { className: 'runner running' },
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
);
this.document.body.appendChild(this.outerDiv);
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var suiteDiv = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
this.suiteDivs[suite.id] = suiteDiv;
var parentDiv = this.outerDiv;
if (suite.parentSuite) {
parentDiv = this.suiteDivs[suite.parentSuite.id];
}
parentDiv.appendChild(suiteDiv);
}
this.startedAt = new Date();
var self = this;
showPassed.onclick = function(evt) {
if (showPassed.checked) {
self.outerDiv.className += ' show-passed';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
}
};
showSkipped.onclick = function(evt) {
if (showSkipped.checked) {
self.outerDiv.className += ' show-skipped';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
}
};
};
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
var results = runner.results();
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
this.runnerDiv.setAttribute("class", className);
//do it twice for IE
this.runnerDiv.setAttribute("className", className);
var specs = runner.specs();
var specCount = 0;
for (var i = 0; i < specs.length; i++) {
if (this.specFilter(specs[i])) {
specCount++;
}
}
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
var results = suite.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.totalCount === 0) { // todo: change this to check results.skipped
status = 'skipped';
}
this.suiteDivs[suite.id].className += " " + status;
};
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
if (this.logRunningSpecs) {
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
var results = spec.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
var specDiv = this.createDom('div', { className: 'spec ' + status },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(spec.getFullName()),
title: spec.getFullName()
}, spec.description));
var resultItems = results.getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
specDiv.appendChild(messagesDiv);
}
this.suiteDivs[spec.suite.id].appendChild(specDiv);
};
jasmine.TrivialReporter.prototype.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
jasmine.TrivialReporter.prototype.getLocation = function() {
return this.document.location;
};
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
var paramMap = {};
var params = this.getLocation().search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
if (!paramMap.spec) {
return true;
}
return spec.getFullName().indexOf(paramMap.spec) === 0;
};

View File

@ -1,82 +0,0 @@
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
#HTMLReporter a { text-decoration: none; }
#HTMLReporter a:hover { text-decoration: underline; }
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
#HTMLReporter .version { color: #aaaaaa; }
#HTMLReporter .banner { margin-top: 14px; }
#HTMLReporter .duration { color: #aaaaaa; float: right; }
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
#HTMLReporter .runningAlert { background-color: #666666; }
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
#HTMLReporter .passingAlert { background-color: #a6b779; }
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
#HTMLReporter .failingAlert { background-color: #cf867e; }
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
#HTMLReporter .results { margin-top: 14px; }
#HTMLReporter #details { display: none; }
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter.showDetails .summary { display: none; }
#HTMLReporter.showDetails #details { display: block; }
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter .summary { margin-top: 14px; }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
#HTMLReporter .description + .suite { margin-top: 0; }
#HTMLReporter .suite { margin-top: 14px; }
#HTMLReporter .suite a { color: #333333; }
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
#HTMLReporter .resultMessage span.result { display: block; }
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
#TrivialReporter .runner.running { background-color: yellow; }
#TrivialReporter .options { text-align: right; font-size: .8em; }
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
#TrivialReporter .suite .suite { margin: 5px; }
#TrivialReporter .suite.passed { background-color: #dfd; }
#TrivialReporter .suite.failed { background-color: #fdd; }
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
#TrivialReporter .spec.skipped { background-color: #bbb; }
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
#TrivialReporter .passed { background-color: #cfc; display: none; }
#TrivialReporter .failed { background-color: #fbb; }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
#TrivialReporter .resultMessage .mismatch { color: black; }
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,211 +0,0 @@
/*! TinySort 1.4.29
* Copyright (c) 2008-2012 Ron Valstar http://www.sjeiti.com/
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*//*
* Description:
* A jQuery plugin to sort child nodes by (sub) contents or attributes.
*
* Contributors:
* brian.gibson@gmail.com
* michael.thornberry@gmail.com
*
* Usage:
* $("ul#people>li").tsort();
* $("ul#people>li").tsort("span.surname");
* $("ul#people>li").tsort("span.surname",{order:"desc"});
* $("ul#people>li").tsort({place:"end"});
*
* Change default like so:
* $.tinysort.defaults.order = "desc";
*
* in this update:
* - added plugin hook
* - stripped non-latin character ordering and turned it into a plugin
*
* in last update:
* - header comment no longer stripped in minified version
* - revision number no longer corresponds to svn revision since it's now git
*
* Todos:
* - todo: uppercase vs lowercase
* - todo: 'foobar' != 'foobars' in non-latin
*
*/
;(function($) {
// private vars
var fls = !1 // minify placeholder
,nll = null // minify placeholder
,prsflt = parseFloat // minify placeholder
,mathmn = Math.min // minify placeholder
,rxLastNr = /(-?\d+\.?\d*)$/g // regex for testing strings ending on numbers
,aPluginPrepare = []
,aPluginSort = []
;
//
// init plugin
$.tinysort = {
id: 'TinySort'
,version: '1.4.29'
,copyright: 'Copyright (c) 2008-2012 Ron Valstar'
,uri: 'http://tinysort.sjeiti.com/'
,licensed: {
MIT: 'http://www.opensource.org/licenses/mit-license.php'
,GPL: 'http://www.gnu.org/licenses/gpl.html'
}
,plugin: function(prepare,sort){
aPluginPrepare.push(prepare); // function(settings){doStuff();}
aPluginSort.push(sort); // function(valuesAreNumeric,sA,sB,iReturn){doStuff();return iReturn;}
}
,defaults: { // default settings
order: 'asc' // order: asc, desc or rand
,attr: nll // order by attribute value
,data: nll // use the data attribute for sorting
,useVal: fls // use element value instead of text
,place: 'start' // place ordered elements at position: start, end, org (original position), first
,returns: fls // return all elements or only the sorted ones (true/false)
,cases: fls // a case sensitive sort orders [aB,aa,ab,bb]
,forceStrings:fls // if false the string '2' will sort with the value 2, not the string '2'
,sortFunction: nll // override the default sort function
}
};
$.fn.extend({
tinysort: function(_find,_settings) {
if (_find&&typeof(_find)!='string') {
_settings = _find;
_find = nll;
}
var oSettings = $.extend({}, $.tinysort.defaults, _settings)
,sParent
,oThis = this
,iLen = $(this).length
,oElements = {} // contains sortable- and non-sortable list per parent
,bFind = !(!_find||_find=='')
,bAttr = !(oSettings.attr===nll||oSettings.attr=="")
,bData = oSettings.data!==nll
// since jQuery's filter within each works on array index and not actual index we have to create the filter in advance
,bFilter = bFind&&_find[0]==':'
,$Filter = bFilter?oThis.filter(_find):oThis
,fnSort = oSettings.sortFunction
,iAsc = oSettings.order=='asc'?1:-1
,aNewOrder = []
;
$.each(aPluginPrepare,function(i,fn){
fn.call(fn,oSettings);
});
if (!fnSort) fnSort = oSettings.order=='rand'?function() {
return Math.random()<.5?1:-1;
}:function(a,b) {
var bNumeric = fls
// maybe toLower
,sA = !oSettings.cases?toLowerCase(a.s):a.s
,sB = !oSettings.cases?toLowerCase(b.s):b.s;
// maybe force Strings
// var bAString = typeof(sA)=='string';
// var bBString = typeof(sB)=='string';
// if (!oSettings.forceStrings&&(bAString||bBString)) {
// if (!bAString) sA = ''+sA;
// if (!bBString) sB = ''+sB;
if (!oSettings.forceStrings) {
// maybe mixed
var aAnum = sA&&sA.match(rxLastNr)
,aBnum = sB&&sB.match(rxLastNr);
if (aAnum&&aBnum) {
var sAprv = sA.substr(0,sA.length-aAnum[0].length)
,sBprv = sB.substr(0,sB.length-aBnum[0].length);
if (sAprv==sBprv) {
bNumeric = !fls;
sA = prsflt(aAnum[0]);
sB = prsflt(aBnum[0]);
}
}
}
// return sort-integer
var iReturn = iAsc*(sA<sB?-1:(sA>sB?1:0));
$.each(aPluginSort,function(i,fn){
iReturn = fn.call(fn,bNumeric,sA,sB,iReturn);
});
return iReturn;
};
oThis.each(function(i,el) {
var $Elm = $(el)
// element or sub selection
,mElmOrSub = bFind?(bFilter?$Filter.filter(el):$Elm.find(_find)):$Elm
// text or attribute value
,sSort = bData?''+mElmOrSub.data(oSettings.data):(bAttr?mElmOrSub.attr(oSettings.attr):(oSettings.useVal?mElmOrSub.val():mElmOrSub.text()))
// to sort or not to sort
,mParent = $Elm.parent();
if (!oElements[mParent]) oElements[mParent] = {s:[],n:[]}; // s: sort, n: not sort
if (mElmOrSub.length>0) oElements[mParent].s.push({s:sSort,e:$Elm,n:i}); // s:string, e:element, n:number
else oElements[mParent].n.push({e:$Elm,n:i});
});
//
// sort
for (sParent in oElements) oElements[sParent].s.sort(fnSort);
//
// order elements and fill new order
for (sParent in oElements) {
var oParent = oElements[sParent]
,aOrg = [] // list for original position
,iLow = iLen
,aCnt = [0,0] // count how much we've sorted for retreival from either the sort list or the non-sort list (oParent.s/oParent.n)
,i;
switch (oSettings.place) {
case 'first': $.each(oParent.s,function(i,obj) { iLow = mathmn(iLow,obj.n) }); break;
case 'org': $.each(oParent.s,function(i,obj) { aOrg.push(obj.n) }); break;
case 'end': iLow = oParent.n.length; break;
default: iLow = 0;
}
for (i = 0;i<iLen;i++) {
var bSList = contains(aOrg,i)?!fls:i>=iLow&&i<iLow+oParent.s.length
,mEl = (bSList?oParent.s:oParent.n)[aCnt[bSList?0:1]].e;
mEl.parent().append(mEl);
if (bSList||!oSettings.returns) aNewOrder.push(mEl.get(0));
aCnt[bSList?0:1]++;
}
}
oThis.length = 0;
Array.prototype.push.apply(oThis,aNewOrder);
return oThis;
}
});
// toLowerCase
function toLowerCase(s) {
return s&&s.toLowerCase?s.toLowerCase():s;
}
// array contains
function contains(a,n) {
for (var i=0,l=a.length;i<l;i++) if (a[i]==n) return !fls;
return fls;
}
// set functions
$.fn.TinySort = $.fn.Tinysort = $.fn.tsort = $.fn.tinysort;
})(jQuery);
/*! Array.prototype.indexOf for IE (issue #26) */
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(elt /*, from*/) {
var len = this.length
,from = Number(arguments[1])||0;
from = from<0?Math.ceil(from):Math.floor(from);
if (from<0) from += len;
for (;from<len;from++){
if (from in this && this[from]===elt) return from;
}
return -1;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +0,0 @@
"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
if(typeof module!="undefined"&&module.exports)module.exports=sjcl;
sjcl.cipher.aes=function(a){this.h[0][0][0]||this.z();var b,c,d,e,f=this.h[0][4],g=this.h[1];b=a.length;var h=1;if(b!==4&&b!==6&&b!==8)throw new sjcl.exception.invalid("invalid aes key size");this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(a%b===0||b===8&&a%b===4){c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255];if(a%b===0){c=c<<8^c>>>24^h<<24;h=h<<1^(h>>7)*283}}d[a]=d[a-b]^c}for(b=0;a;b++,a--){c=d[b&3?a:a-4];e[b]=a<=4||b<4?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^
g[3][f[c&255]]}};
sjcl.cipher.aes.prototype={encrypt:function(a){return this.I(a,0)},decrypt:function(a){return this.I(a,1)},h:[[[],[],[],[],[]],[[],[],[],[],[]]],z:function(){var a=this.h[0],b=this.h[1],c=a[4],d=b[4],e,f,g,h=[],i=[],k,j,l,m;for(e=0;e<0x100;e++)i[(h[e]=e<<1^(e>>7)*283)^e]=e;for(f=g=0;!c[f];f^=k||1,g=i[g]||1){l=g^g<<1^g<<2^g<<3^g<<4;l=l>>8^l&255^99;c[f]=l;d[l]=f;j=h[e=h[k=h[f]]];m=j*0x1010101^e*0x10001^k*0x101^f*0x1010100;j=h[l]*0x101^l*0x1010100;for(e=0;e<4;e++){a[e][f]=j=j<<24^j>>>8;b[e][l]=m=m<<24^m>>>8}}for(e=
0;e<5;e++){a[e]=a[e].slice(0);b[e]=b[e].slice(0)}},I:function(a,b){if(a.length!==4)throw new sjcl.exception.invalid("invalid aes block size");var c=this.a[b],d=a[0]^c[0],e=a[b?3:1]^c[1],f=a[2]^c[2];a=a[b?1:3]^c[3];var g,h,i,k=c.length/4-2,j,l=4,m=[0,0,0,0];g=this.h[b];var n=g[0],o=g[1],p=g[2],q=g[3],r=g[4];for(j=0;j<k;j++){g=n[d>>>24]^o[e>>16&255]^p[f>>8&255]^q[a&255]^c[l];h=n[e>>>24]^o[f>>16&255]^p[a>>8&255]^q[d&255]^c[l+1];i=n[f>>>24]^o[a>>16&255]^p[d>>8&255]^q[e&255]^c[l+2];a=n[a>>>24]^o[d>>16&
255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(j=0;j<4;j++){m[b?3&-j:j]=r[d>>>24]<<24^r[e>>16&255]<<16^r[f>>8&255]<<8^r[a&255]^c[l++];g=d;d=e;e=f;f=a;a=g}return m}};
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===undefined?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(a.length===0||b.length===0)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return d===32?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;
if(b===0)return 0;return(b-1)*32+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(a.length*32<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;if(c>0&&b)a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1);return a},partial:function(a,b,c){if(a===32)return b;return(c?b|0:b<<32-a)+a*0x10000000000},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return false;var c=0,d;for(d=0;d<a.length;d++)c|=
a[d]^b[d];return c===0},P:function(a,b,c,d){var e;e=0;if(d===undefined)d=[];for(;b>=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a);for(e=0;e<a.length;e++){d.push(c|a[e]>>>b);c=a[e]<<32-b}e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,b+a>32?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++){if((d&3)===0)e=a[d/4];b+=String.fromCharCode(e>>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++){d=d<<8|a.charCodeAt(c);if((c&3)===3){b.push(d);d=0}}c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,d*4)}};
sjcl.codec.base64={F:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.F,g=0,h=sjcl.bitArray.bitLength(a);if(c)f=f.substr(0,62)+"-_";for(c=0;d.length*6<h;){d+=f.charAt((g^a[c]>>>e)>>>26);if(e<6){g=a[c]<<6-e;e+=26;c++}else{g<<=6;e-=6}}for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d=0,e=sjcl.codec.base64.F,f=0,g;if(b)e=e.substr(0,62)+"-_";for(b=0;b<a.length;b++){g=e.indexOf(a.charAt(b));
if(g<0)throw new sjcl.exception.invalid("this isn't base64!");if(d>26){d-=26;c.push(f^g>>>d);f=g<<32-d}else{d+=6;f^=g<<32-d}}d&56&&c.push(sjcl.bitArray.partial(d&56,f,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.z();if(a){this.n=a.n.slice(0);this.i=a.i.slice(0);this.e=a.e}else this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.n=this.N.slice(0);this.i=[];this.e=0;return this},update:function(a){if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);var b,c=this.i=sjcl.bitArray.concat(this.i,a);b=this.e;a=this.e=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.D(c.splice(0,16));return this},finalize:function(){var a,b=this.i,c=this.n;b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.e/
4294967296));for(b.push(this.e|0);b.length;)this.D(b.splice(0,16));this.reset();return c},N:[],a:[],z:function(){function a(e){return(e-Math.floor(e))*0x100000000|0}var b=0,c=2,d;a:for(;b<64;c++){for(d=2;d*d<=c;d++)if(c%d===0)continue a;if(b<8)this.N[b]=a(Math.pow(c,0.5));this.a[b]=a(Math.pow(c,1/3));b++}},D:function(a){var b,c,d=a.slice(0),e=this.n,f=this.a,g=e[0],h=e[1],i=e[2],k=e[3],j=e[4],l=e[5],m=e[6],n=e[7];for(a=0;a<64;a++){if(a<16)b=d[a];else{b=d[a+1&15];c=d[a+14&15];b=d[a&15]=(b>>>7^b>>>18^
b>>>3^b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+d[a&15]+d[a+9&15]|0}b=b+n+(j>>>6^j>>>11^j>>>25^j<<26^j<<21^j<<7)+(m^j&(l^m))+f[a];n=m;m=l;l=j;j=k+b|0;k=i;i=h;h=g;g=b+(h&i^k&(h^i))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0}e[0]=e[0]+g|0;e[1]=e[1]+h|0;e[2]=e[2]+i|0;e[3]=e[3]+k|0;e[4]=e[4]+j|0;e[5]=e[5]+l|0;e[6]=e[6]+m|0;e[7]=e[7]+n|0}};
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,i=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];if(i<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;f<4&&k>>>8*f;f++);if(f<15-i)f=15-i;c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.H(a,b,c,d,e,f);g=sjcl.mode.ccm.J(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),i=f.clamp(b,h-e),k=f.bitSlice(b,
h-e);h=(h-e)/8;if(g<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;b<4&&h>>>8*b;b++);if(b<15-g)b=15-g;c=f.clamp(c,8*(15-b));i=sjcl.mode.ccm.J(a,i,c,k,e,b);a=sjcl.mode.ccm.H(a,i.data,c,d,e,b);if(!f.equal(i.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");return i.data},H:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,i=h.k;e/=8;if(e%2||e<4||e>16)throw new sjcl.exception.invalid("ccm: invalid tag length");if(d.length>0xffffffff||b.length>0xffffffff)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;if(c<=65279)g=[h.partial(16,c)];else if(c<=0xffffffff)g=h.concat([h.partial(16,65534)],[c]);g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(i(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(i(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,e*8)},J:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var i=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!i)return{tag:d,data:[]};for(g=0;g<i;g+=4){c[3]++;e=a.encrypt(c);b[g]^=e[0];b[g+1]^=e[1];b[g+2]^=e[2];b[g+3]^=e[3]}return{tag:d,data:h.clamp(b,k)}}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.B,i=sjcl.bitArray,k=i.k,j=[0,0,0,0];c=h(a.encrypt(c));var l,m=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4){l=b.slice(g,g+4);j=k(j,l);m=m.concat(k(c,a.encrypt(k(c,l))));c=h(c)}l=b.slice(g);b=i.bitLength(l);g=a.encrypt(k(c,[0,0,0,b]));l=i.clamp(k(l.concat([0,0,0]),g),b);j=k(j,k(l.concat([0,0,0]),g));j=a.encrypt(k(j,k(c,h(c))));
if(d.length)j=k(j,f?d:sjcl.mode.ocb2.pmac(a,d));return m.concat(i.concat(l,i.clamp(j,e)))},decrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");e=e||64;var g=sjcl.mode.ocb2.B,h=sjcl.bitArray,i=h.k,k=[0,0,0,0],j=g(a.encrypt(c)),l,m,n=sjcl.bitArray.bitLength(b)-e,o=[];d=d||[];for(c=0;c+4<n/32;c+=4){l=i(j,a.decrypt(i(j,b.slice(c,c+4))));k=i(k,l);o=o.concat(l);j=g(j)}m=n-c*32;l=a.encrypt(i(j,[0,0,0,m]));l=i(l,h.clamp(b.slice(c),
m).concat([0,0,0]));k=i(k,l);k=a.encrypt(i(k,i(j,g(j))));if(d.length)k=i(k,f?d:sjcl.mode.ocb2.pmac(a,d));if(!h.equal(h.clamp(k,e),h.bitSlice(b,n)))throw new sjcl.exception.corrupt("ocb: tag doesn't match");return o.concat(h.clamp(l,m))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.B,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]);h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4){h=d(h);g=f(g,a.encrypt(f(h,b.slice(c,c+4))))}b=b.slice(c);if(e.bitLength(b)<128){h=f(h,d(h));b=e.concat(b,[2147483648|0,0,
0,0])}g=f(g,b);return a.encrypt(f(d(f(h,d(h))),g))},B:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d=b.prototype.blockSize/32;this.l=[new b,new b];if(a.length>d)a=b.hash(a);for(b=0;b<d;b++){c[0][b]=a[b]^909522486;c[1][b]=a[b]^1549556828}this.l[0].update(c[0]);this.l[1].update(c[1])};
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a,b){a=(new this.M(this.l[0])).update(a,b).finalize();return(new this.M(this.l[1])).update(a).finalize()};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;if(d<0||c<0)throw sjcl.exception.invalid("invalid params to pbkdf2");if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,i,k=[],j=sjcl.bitArray;for(i=1;32*k.length<(d||1);i++){e=f=a.encrypt(j.concat(b,[i]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}if(d)k=j.clamp(k,d);return k};
sjcl.random={randomWords:function(a,b){var c=[];b=this.isReady(b);var d;if(b===0)throw new sjcl.exception.notReady("generator isn't seeded");else b&2&&this.U(!(b&1));for(b=0;b<a;b+=4){(b+1)%0x10000===0&&this.L();d=this.w();c.push(d[0],d[1],d[2],d[3])}this.L();return c.slice(0,a)},setDefaultParanoia:function(a){this.t=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.q[c],h=this.isReady(),i=0;d=this.G[c];if(d===undefined)d=this.G[c]=this.R++;if(g===undefined)g=this.q[c]=
0;this.q[c]=(this.q[c]+1)%this.b.length;switch(typeof a){case "number":if(b===undefined)b=1;this.b[g].update([d,this.u++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if(c==="[object Uint32Array]"){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{if(c!=="[object Array]")i=1;for(c=0;c<a.length&&!i;c++)if(typeof a[c]!="number")i=1}if(!i){if(b===undefined)for(c=b=0;c<a.length;c++)for(e=a[c];e>0;){b++;e>>>=1}this.b[g].update([d,this.u++,2,b,f,a.length].concat(a))}break;case "string":if(b===
undefined)b=a.length;this.b[g].update([d,this.u++,3,b,f,a.length]);this.b[g].update(a);break;default:i=1}if(i)throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");this.j[g]+=b;this.f+=b;if(h===0){this.isReady()!==0&&this.K("seeded",Math.max(this.g,this.f));this.K("progress",this.getProgress())}},isReady:function(a){a=this.C[a!==undefined?a:this.t];return this.g&&this.g>=a?this.j[0]>80&&(new Date).valueOf()>this.O?3:1:this.f>=a?2:0},getProgress:function(a){a=
this.C[a?a:this.t];return this.g>=a?1:this.f>a?1:this.f/a},startCollectors:function(){if(!this.m){if(window.addEventListener){window.addEventListener("load",this.o,false);window.addEventListener("mousemove",this.p,false)}else if(document.attachEvent){document.attachEvent("onload",this.o);document.attachEvent("onmousemove",this.p)}else throw new sjcl.exception.bug("can't attach event");this.m=true}},stopCollectors:function(){if(this.m){if(window.removeEventListener){window.removeEventListener("load",
this.o,false);window.removeEventListener("mousemove",this.p,false)}else if(window.detachEvent){window.detachEvent("onload",this.o);window.detachEvent("onmousemove",this.p)}this.m=false}},addEventListener:function(a,b){this.r[a][this.Q++]=b},removeEventListener:function(a,b){var c;a=this.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&a[c]===b&&d.push(c);for(b=0;b<d.length;b++){c=d[b];delete a[c]}},b:[new sjcl.hash.sha256],j:[0],A:0,q:{},u:0,G:{},R:0,g:0,f:0,O:0,a:[0,0,0,0,0,0,0,0],d:[0,0,0,0],s:undefined,
t:6,m:false,r:{progress:{},seeded:{}},Q:0,C:[0,48,64,96,128,192,0x100,384,512,768,1024],w:function(){for(var a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}return this.s.encrypt(this.d)},L:function(){this.a=this.w().concat(this.w());this.s=new sjcl.cipher.aes(this.a)},T:function(a){this.a=sjcl.hash.sha256.hash(this.a.concat(a));this.s=new sjcl.cipher.aes(this.a);for(a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}},U:function(a){var b=[],c=0,d;this.O=b[0]=(new Date).valueOf()+3E4;for(d=
0;d<16;d++)b.push(Math.random()*0x100000000|0);for(d=0;d<this.b.length;d++){b=b.concat(this.b[d].finalize());c+=this.j[d];this.j[d]=0;if(!a&&this.A&1<<d)break}if(this.A>=1<<this.b.length){this.b.push(new sjcl.hash.sha256);this.j.push(0)}this.f-=c;if(c>this.g)this.g=c;this.A++;this.T(b)},p:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX||0,a.y||a.clientY||a.offsetY||0],2,"mouse")},o:function(){sjcl.random.addEntropy((new Date).valueOf(),2,"loadtime")},K:function(a,b){var c;a=sjcl.random.r[a];
var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c<d.length;c++)d[c](b)}};try{var s=new Uint32Array(32);crypto.getRandomValues(s);sjcl.random.addEntropy(s,1024,"crypto['getRandomValues']")}catch(t){}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.c({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.c(f,c);c=f.adata;if(typeof f.salt==="string")f.salt=sjcl.codec.base64.toBits(f.salt);if(typeof f.iv==="string")f.iv=sjcl.codec.base64.toBits(f.iv);if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||typeof a==="string"&&f.iter<=100||f.ts!==64&&f.ts!==96&&f.ts!==128||f.ks!==128&&f.ks!==192&&f.ks!==0x100||f.iv.length<
2||f.iv.length>4)throw new sjcl.exception.invalid("json encrypt: invalid parameters");if(typeof a==="string"){g=sjcl.misc.cachedPbkdf2(a,f);a=g.key.slice(0,f.ks/32);f.salt=g.salt}if(typeof b==="string")b=sjcl.codec.utf8String.toBits(b);if(typeof c==="string")c=sjcl.codec.utf8String.toBits(c);g=new sjcl.cipher[f.cipher](a);e.c(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(f)},decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.c(e.c(e.c({},e.defaults),e.decode(b)),
c,true);var f;c=b.adata;if(typeof b.salt==="string")b.salt=sjcl.codec.base64.toBits(b.salt);if(typeof b.iv==="string")b.iv=sjcl.codec.base64.toBits(b.iv);if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||typeof a==="string"&&b.iter<=100||b.ts!==64&&b.ts!==96&&b.ts!==128||b.ks!==128&&b.ks!==192&&b.ks!==0x100||!b.iv||b.iv.length<2||b.iv.length>4)throw new sjcl.exception.invalid("json decrypt: invalid parameters");if(typeof a==="string"){f=sjcl.misc.cachedPbkdf2(a,b);a=f.key.slice(0,b.ks/32);b.salt=f.salt}if(typeof c===
"string")c=sjcl.codec.utf8String.toBits(c);f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.c(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+
sjcl.codec.base64.fromBits(a[b],1)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type");}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++){if(!(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");b[d[2]]=
d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4])}return b},c:function(a,b,c){if(a===undefined)a={};if(b===undefined)return a;var d;for(d in b)if(b.hasOwnProperty(d)){if(c&&a[d]!==undefined&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},W:function(a,b){var c={},d;for(d in a)if(a.hasOwnProperty(d)&&a[d]!==b[d])c[d]=a[d];return c},V:function(a,b){var c={},d;for(d=0;d<b.length;d++)if(a[b[d]]!==undefined)c[b[d]]=
a[b[d]];return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.S={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.S,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===undefined?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};

View File

@ -1,232 +0,0 @@
/*
Copyright 2010, François de Metz <francois@2metz.fr>
*/
/**
* Disco Strophe Plugin
* Implement http://xmpp.org/extensions/xep-0030.html
* TODO: manage node hierarchies, and node on info request
*/
Strophe.addConnectionPlugin('disco',
{
_connection: null,
_identities : [],
_features : [],
_items : [],
/** Function: init
* Plugin init
*
* Parameters:
* (Strophe.Connection) conn - Strophe connection
*/
init: function(conn)
{
this._connection = conn;
this._identities = [];
this._features = [];
this._items = [];
// disco info
conn.addHandler(this._onDiscoInfo.bind(this), Strophe.NS.DISCO_INFO, 'iq', 'get', null, null);
// disco items
conn.addHandler(this._onDiscoItems.bind(this), Strophe.NS.DISCO_ITEMS, 'iq', 'get', null, null);
},
/** Function: addIdentity
* See http://xmpp.org/registrar/disco-categories.html
* Parameters:
* (String) category - category of identity (like client, automation, etc ...)
* (String) type - type of identity (like pc, web, bot , etc ...)
* (String) name - name of identity in natural language
* (String) lang - lang of name parameter
*
* Returns:
* Boolean
*/
addIdentity: function(category, type, name, lang)
{
for (var i=0; i<this._identities.length; i++)
{
if (this._identities[i].category == category &&
this._identities[i].type == type &&
this._identities[i].name == name &&
this._identities[i].lang == lang)
{
return false;
}
}
this._identities.push({category: category, type: type, name: name, lang: lang});
return true;
},
/** Function: addFeature
*
* Parameters:
* (String) var_name - feature name (like jabber:iq:version)
*
* Returns:
* boolean
*/
addFeature: function(var_name)
{
for (var i=0; i<this._features.length; i++)
{
if (this._features[i] == var_name)
return false;
}
this._features.push(var_name);
return true;
},
/** Function: removeFeature
*
* Parameters:
* (String) var_name - feature name (like jabber:iq:version)
*
* Returns:
* boolean
*/
removeFeature: function(var_name)
{
for (var i=0; i<this._features.length; i++)
{
if (this._features[i] === var_name){
this._features.splice(i,1)
return true;
}
}
return false;
},
/** Function: addItem
*
* Parameters:
* (String) jid
* (String) name
* (String) node
* (Function) call_back
*
* Returns:
* boolean
*/
addItem: function(jid, name, node, call_back)
{
if (node && !call_back)
return false;
this._items.push({jid: jid, name: name, node: node, call_back: call_back});
return true;
},
/** Function: info
* Info query
*
* Parameters:
* (Function) call_back
* (String) jid
* (String) node
*/
info: function(jid, node, success, error, timeout)
{
var attrs = {xmlns: Strophe.NS.DISCO_INFO};
if (node)
attrs.node = node;
var info = $iq({from:this._connection.jid,
to:jid, type:'get'}).c('query', attrs);
this._connection.sendIQ(info, success, error, timeout);
},
/** Function: items
* Items query
*
* Parameters:
* (Function) call_back
* (String) jid
* (String) node
*/
items: function(jid, node, success, error, timeout)
{
var attrs = {xmlns: Strophe.NS.DISCO_ITEMS};
if (node)
attrs.node = node;
var items = $iq({from:this._connection.jid,
to:jid, type:'get'}).c('query', attrs);
this._connection.sendIQ(items, success, error, timeout);
},
/** PrivateFunction: _buildIQResult
*/
_buildIQResult: function(stanza, query_attrs)
{
var id = stanza.getAttribute('id');
var from = stanza.getAttribute('from');
var iqresult = $iq({type: 'result', id: id});
if (from !== null) {
iqresult.attrs({to: from});
}
return iqresult.c('query', query_attrs);
},
/** PrivateFunction: _onDiscoInfo
* Called when receive info request
*/
_onDiscoInfo: function(stanza)
{
var node = stanza.getElementsByTagName('query')[0].getAttribute('node');
var attrs = {xmlns: Strophe.NS.DISCO_INFO};
if (node)
{
attrs.node = node;
}
var iqresult = this._buildIQResult(stanza, attrs);
for (var i=0; i<this._identities.length; i++)
{
var attrs = {category: this._identities[i].category,
type : this._identities[i].type};
if (this._identities[i].name)
attrs.name = this._identities[i].name;
if (this._identities[i].lang)
attrs['xml:lang'] = this._identities[i].lang;
iqresult.c('identity', attrs).up();
}
for (var i=0; i<this._features.length; i++)
{
iqresult.c('feature', {'var':this._features[i]}).up();
}
this._connection.send(iqresult.tree());
return true;
},
/** PrivateFunction: _onDiscoItems
* Called when receive items request
*/
_onDiscoItems: function(stanza)
{
var query_attrs = {xmlns: Strophe.NS.DISCO_ITEMS};
var node = stanza.getElementsByTagName('query')[0].getAttribute('node');
if (node)
{
query_attrs.node = node;
var items = [];
for (var i = 0; i < this._items.length; i++)
{
if (this._items[i].node == node)
{
items = this._items[i].call_back(stanza);
break;
}
}
}
else
{
var items = this._items;
}
var iqresult = this._buildIQResult(stanza, query_attrs);
for (var i = 0; i < items.length; i++)
{
var attrs = {jid: items[i].jid};
if (items[i].name)
attrs.name = items[i].name;
if (items[i].node)
attrs.node = items[i].node;
iqresult.c('item', attrs).up();
}
this._connection.send(iqresult.tree());
return true;
}
});

File diff suppressed because it is too large Load Diff

View File

@ -1,963 +0,0 @@
/*
*Plugin to implement the MUC extension.
http://xmpp.org/extensions/xep-0045.html
*Previous Author:
Nathan Zorn <nathan.zorn@gmail.com>
*Complete CoffeeScript rewrite:
Andreas Guth <guth@dbis.rwth-aachen.de>
*Cleanup, AMD/global registrations and bugfixes:
JC Brand <jc@opkode.com>
*/
// AMD/global registrations
(function (root, factory) {
if (typeof console === undefined || typeof console.log === undefined) {
console = { log: function () {}, error: function () {} };
}
if (typeof define === 'function' && define.amd) {
define([
"strophe"
], function () {
if (console===undefined || console.log===undefined) {
console = { log: function () {}, error: function () {} };
}
return factory(jQuery, console);
}
);
} else {
return factory(jQuery, console);
}
}(this, function ($, console) {
(function() {
var Occupant, RoomConfig, XmppRoom,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Strophe.addConnectionPlugin('muc', {
_connection: null,
rooms: [],
init: function(conn) {
/* Initialize the MUC plugin. Sets the correct connection object and
* extends the namesace.
*/
this._connection = conn;
this._muc_handler = null;
Strophe.addNamespace('MUC_OWNER', Strophe.NS.MUC + "#owner");
Strophe.addNamespace('MUC_ADMIN', Strophe.NS.MUC + "#admin");
Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + "#user");
return Strophe.addNamespace('MUC_ROOMCONF', Strophe.NS.MUC + "#roomconfig");
},
join: function(room, nick, msg_handler_cb, pres_handler_cb, roster_cb, password) {
/* Join a multi-user chat room
*
* Parameters:
* (String) room - The multi-user chat room to join.
* (String) nick - Optional nickname to use in the chat room.
* (Function) msg_handler_cb - The function call to handle messages from the specified chat room.
* (Function) pres_handler_cb - The function call back to handle presence in the chat room.
* (Function) roster_cb - The function call back to handle roster changes in the chat room.
* (String) password - The optional password to use. (password protected rooms only)
*/
var msg, room_nick, _this = this;
room_nick = this.test_append_nick(room, nick);
msg = $pres({
from: this._connection.jid,
to: room_nick
}).c("x", {
xmlns: Strophe.NS.MUC
});
if (password !== null) {
msg.cnode(Strophe.xmlElement("password", [], password));
}
if (this._muc_handler === null) {
this._muc_handler = this._connection.addHandler(function(stanza) {
var from, handler, handlers, id, roomname, x, xmlns, xquery, _i, _len;
from = stanza.getAttribute('from');
if (!from) { return true; }
roomname = from.split("/")[0];
if (!_this.rooms[roomname]) { return true; }
room = _this.rooms[roomname];
handlers = {};
if (stanza.nodeName === "message") {
handlers = room._message_handlers;
} else if (stanza.nodeName === "presence") {
xquery = stanza.getElementsByTagName("x");
if (xquery.length > 0) {
for (_i = 0, _len = xquery.length; _i < _len; _i++) {
x = xquery[_i];
xmlns = x.getAttribute("xmlns");
if (xmlns && xmlns.match(Strophe.NS.MUC)) {
handlers = room._presence_handlers;
break;
}
}
}
}
_.each(handlers, function (handler, id, handlers) {
if (!handler(stanza, room)) { delete handlers[id]; }
});
return true;
});
}
if (!_.has(this.rooms, room)) {
this.rooms[room] = new XmppRoom(this, room, nick, password);
}
if (pres_handler_cb) {
this.rooms[room].addHandler('presence', pres_handler_cb);
}
if (msg_handler_cb) {
this.rooms[room].addHandler('message', msg_handler_cb);
}
if (roster_cb) {
this.rooms[room].addHandler('roster', roster_cb);
}
return this._connection.send(msg);
},
removeRoom: function (room) {
delete this.rooms[room];
if (this.rooms.length === 0) {
this._connection.deleteHandler(this._muc_handler);
this._muc_handler = null;
}
},
leave: function(room, nick, handler_cb, exit_msg) {
/* Leave a multi-user chat room
*
* Parameters:
* (String) room - The multi-user chat room to leave.
* (String) nick - The nick name used in the room.
* (Function) handler_cb - Optional function to handle the successful leave.
* (String) exit_msg - optional exit message.
* Returns:
* iqid - The unique id for the room leave.
*/
var presence, presenceid, room_nick;
this.removeRoom(room);
room_nick = this.test_append_nick(room, nick);
presenceid = this._connection.getUniqueId();
presence = $pres({
type: "unavailable",
id: presenceid,
from: this._connection.jid,
to: room_nick
});
if (exit_msg !== null) {
presence.c("status", exit_msg);
}
if (handler_cb !== null) {
this._connection.addHandler(handler_cb, null, "presence", null, presenceid);
}
this._connection.send(presence);
return presenceid;
},
message: function(room, nick, message, html_message, type) {
/* Parameters:
* (String) room - The multi-user chat room name.
* (String) nick - The nick name used in the chat room.
* (String) message - The plaintext message to send to the room.
* (String) html_message - The message to send to the room with html markup.
* (String) type - "groupchat" for group chat messages o
* "chat" for private chat messages
* Returns:
* msgiq - the unique id used to send the message
*/
var msg, msgid, parent, room_nick;
room_nick = this.test_append_nick(room, nick);
type = type || (nick !== null ? "chat" : "groupchat");
msgid = this._connection.getUniqueId();
msg = $msg({
to: room_nick,
from: this._connection.jid,
type: type,
id: msgid
}).c("body", {
xmlns: Strophe.NS.CLIENT
}).t(message);
msg.up();
if (html_message != null) {
msg.c("html", {xmlns: Strophe.NS.XHTML_IM}).c("body", {xmlns: Strophe.NS.XHTML}).h(html_message);
if (msg.node.childNodes.length === 0) {
parent = msg.node.parentNode;
msg.up().up();
msg.node.removeChild(parent);
} else {
msg.up().up();
}
}
msg.c("x", {
xmlns: "jabber:x:event"
}).c("composing");
this._connection.send(msg);
return msgid;
},
groupchat: function(room, message, html_message) {
/* Convenience Function to send a Message to all Occupants
* Parameters:
* (String) room - The multi-user chat room name.
* (String) message - The plaintext message to send to the room.
* (String) html_message - The message to send to the room with html markup.
* Returns:
* msgiq - the unique id used to send the message
*/
return this.message(room, null, message, html_message);
},
invite: function(room, receiver, reason) {
/* Send a mediated invitation.
* Parameters:
* (String) room - The multi-user chat room name.
* (String) receiver - The invitation's receiver.
* (String) reason - Optional reason for joining the room.
* Returns:
* msgiq - the unique id used to send the invitation
*/
var invitation, msgid;
msgid = this._connection.getUniqueId();
invitation = $msg({
from: this._connection.jid,
to: room,
id: msgid
}).c('x', {
xmlns: Strophe.NS.MUC_USER
}).c('invite', {
to: receiver
});
if (reason !== null) {
invitation.c('reason', reason);
}
this._connection.send(invitation);
return msgid;
},
directInvite: function(room, receiver, reason, password) {
/* Send a direct invitation.
* Parameters:
* (String) room - The multi-user chat room name.
* (String) receiver - The invitation's receiver.
* (String) reason - Optional reason for joining the room.
* (String) password - Optional password for the room.
* Returns:
* msgiq - the unique id used to send the invitation
*/
var attrs, invitation, msgid;
msgid = this._connection.getUniqueId();
attrs = {
xmlns: 'jabber:x:conference',
jid: room
};
if (reason !== null) { attrs.reason = reason; }
if (password !== null) { attrs.password = password; }
invitation = $msg({
from: this._connection.jid,
to: receiver,
id: msgid
}).c('x', attrs);
this._connection.send(invitation);
return msgid;
},
queryOccupants: function(room, success_cb, error_cb) {
/* Queries a room for a list of occupants
* (String) room - The multi-user chat room name.
* (Function) success_cb - Optional function to handle the info.
* (Function) error_cb - Optional function to handle an error.
* Returns:
* id - the unique id used to send the info request
*/
var attrs, info;
attrs = {
xmlns: Strophe.NS.DISCO_ITEMS
};
info = $iq({
from: this._connection.jid,
to: room,
type: 'get'
}).c('query', attrs);
return this._connection.sendIQ(info, success_cb, error_cb);
},
configure: function(room, handler_cb) {
/* Start a room configuration.
* Parameters:
* (String) room - The multi-user chat room name.
* (Function) handler_cb - Optional function to handle the config form.
* Returns:
* id - the unique id used to send the configuration request
*/
var config, id, stanza;
config = $iq({
to: room,
type: "get"
}).c("query", {
xmlns: Strophe.NS.MUC_OWNER
});
stanza = config.tree();
id = this._connection.sendIQ(stanza);
if (handler_cb !== null) {
this._connection.addHandler(function(stanza) { handler_cb(stanza);
return false;
}, Strophe.NS.MUC_OWNER, "iq", null, id);
}
return id;
},
cancelConfigure: function(room) {
/* Cancel the room configuration
* Parameters:
* (String) room - The multi-user chat room name.
* Returns:
* id - the unique id used to cancel the configuration.
*/
var config, stanza;
config = $iq({
to: room,
type: "set"
}).c("query", {
xmlns: Strophe.NS.MUC_OWNER
}).c("x", {
xmlns: "jabber:x:data",
type: "cancel"
});
stanza = config.tree();
return this._connection.sendIQ(stanza);
},
saveConfiguration: function(room, configarray, callback, errback) {
/* Save a room configuration.
* Parameters:
* (String) room - The multi-user chat room name.
* (Array) configarray - an array of form elements used to configure the room.
* Returns:
* id - the unique id used to save the configuration.
*/
var conf, config, stanza, _i, _len;
config = $iq({
to: room,
type: "set"
}).c("query", {
xmlns: Strophe.NS.MUC_OWNER
}).c("x", {
xmlns: "jabber:x:data",
type: "submit"
});
for (_i = 0, _len = configarray.length; _i < _len; _i++) {
conf = configarray[_i];
config.cnode(conf).up();
}
stanza = config.tree();
return this._connection.sendIQ(stanza, callback, errback);
},
createInstantRoom: function(room) {
/* Parameters:
* (String) room - The multi-user chat room name.
* Returns:
* id - the unique id used to create the chat room.
*/
var roomiq;
roomiq = $iq({
to: room,
type: "set"
}).c("query", {
xmlns: Strophe.NS.MUC_OWNER
}).c("x", {
xmlns: "jabber:x:data",
type: "submit"
});
return this._connection.sendIQ(roomiq.tree());
},
setTopic: function(room, topic) {
/* Set the topic of the chat room.
* Parameters:
* (String) room - The multi-user chat room name.
* (String) topic - Topic message.
*/
var msg;
msg = $msg({
to: room,
from: this._connection.jid,
type: "groupchat"
}).c("subject", {
xmlns: "jabber:client"
}).t(topic);
return this._connection.send(msg.tree());
},
_modifyPrivilege: function(room, item, reason, handler_cb, error_cb) {
/* Internal Function that Changes the role or affiliation of a member
* of a MUC room. This function is used by modifyRole and modifyAffiliation.
* The modification can only be done by a room moderator. An error will be
* returned if the user doesn't have permission.
* Parameters:
* (String) room - The multi-user chat room name.
* (Object) item - Object with nick and role or jid and affiliation attribute
* (String) reason - Optional reason for the change.
* (Function) handler_cb - Optional callback for success
* (Function) errer_cb - Optional callback for error
* Returns:
* iq - the id of the mode change request.
*/
var iq;
iq = $iq({
to: room,
type: "set"
}).c("query", {
xmlns: Strophe.NS.MUC_ADMIN
}).cnode(item.node);
if (reason !== null) { iq.c("reason", reason); }
return this._connection.sendIQ(iq.tree(), handler_cb, error_cb);
},
modifyRole: function(room, nick, role, reason, handler_cb, error_cb) {
/* Changes the role of a member of a MUC room.
* The modification can only be done by a room moderator. An error will be
* returned if the user doesn't have permission.
* Parameters:
* (String) room - The multi-user chat room name.
* (String) nick - The nick name of the user to modify.
* (String) role - The new role of the user.
* (String) affiliation - The new affiliation of the user.
* (String) reason - Optional reason for the change.
* (Function) handler_cb - Optional callback for success
* (Function) errer_cb - Optional callback for error
* Returns:
* iq - the id of the mode change request.
*/
var item;
item = $build("item", {
nick: nick,
role: role
});
return this._modifyPrivilege(room, item, reason, handler_cb, error_cb);
},
kick: function(room, nick, reason, handler_cb, error_cb) {
return this.modifyRole(room, nick, 'none', reason, handler_cb, error_cb);
},
voice: function(room, nick, reason, handler_cb, error_cb) {
return this.modifyRole(room, nick, 'participant', reason, handler_cb, error_cb);
},
mute: function(room, nick, reason, handler_cb, error_cb) {
return this.modifyRole(room, nick, 'visitor', reason, handler_cb, error_cb);
},
op: function(room, nick, reason, handler_cb, error_cb) {
return this.modifyRole(room, nick, 'moderator', reason, handler_cb, error_cb);
},
deop: function(room, nick, reason, handler_cb, error_cb) {
return this.modifyRole(room, nick, 'participant', reason, handler_cb, error_cb);
},
modifyAffiliation: function(room, jid, affiliation, reason, handler_cb, error_cb) {
/* Changes the affiliation of a member of a MUC room.
* The modification can only be done by a room moderator. An error will be
* returned if the user doesn't have permission.
* Parameters:
* (String) room - The multi-user chat room name.
* (String) jid - The jid of the user to modify.
* (String) affiliation - The new affiliation of the user.
* (String) reason - Optional reason for the change.
* (Function) handler_cb - Optional callback for success
* (Function) errer_cb - Optional callback for error
* Returns:
* iq - the id of the mode change request.
*/
var item;
item = $build("item", {
jid: jid,
affiliation: affiliation
});
return this._modifyPrivilege(room, item, reason, handler_cb, error_cb);
},
ban: function(room, jid, reason, handler_cb, error_cb) {
return this.modifyAffiliation(room, jid, 'outcast', reason, handler_cb, error_cb);
},
member: function(room, jid, reason, handler_cb, error_cb) {
return this.modifyAffiliation(room, jid, 'member', reason, handler_cb, error_cb);
},
revoke: function(room, jid, reason, handler_cb, error_cb) {
return this.modifyAffiliation(room, jid, 'none', reason, handler_cb, error_cb);
},
owner: function(room, jid, reason, handler_cb, error_cb) {
return this.modifyAffiliation(room, jid, 'owner', reason, handler_cb, error_cb);
},
admin: function(room, jid, reason, handler_cb, error_cb) {
return this.modifyAffiliation(room, jid, 'admin', reason, handler_cb, error_cb);
},
changeNick: function(room, user) {
/* Change the current users nick name.
* Parameters:
* (String) room - The multi-user chat room name.
* (String) user - The new nick name.
*/
var presence, room_nick;
room_nick = this.test_append_nick(room, user);
presence = $pres({
from: this._connection.jid,
to: room_nick,
id: this._connection.getUniqueId()
});
return this._connection.send(presence.tree());
},
setStatus: function(room, user, show, status) {
/* Change the current users status.
* Parameters:
* (String) room - The multi-user chat room name.
* (String) user - The current nick.
* (String) show - The new show-text.
* (String) status - The new status-text.
*/
var presence, room_nick;
room_nick = this.test_append_nick(room, user);
presence = $pres({
from: this._connection.jid,
to: room_nick
});
if (show !== null) { presence.c('show', show).up(); }
if (status !== null) { presence.c('status', status); }
return this._connection.send(presence.tree());
},
listRooms: function(server, callback, errback) {
/* List all chat room available on a server.
* Parameters:
* (String) server - name of chat server.
* (String) callback - Function to call for room list return.
*/
var iq;
iq = $iq({
to: server,
from: this._connection.jid,
type: "get"
}).c("query", {
xmlns: Strophe.NS.DISCO_ITEMS
});
return this._connection.sendIQ(iq, callback, errback);
},
test_append_nick: function(room, nick) {
return room + (nick !== null ? "/" + (Strophe.escapeNode(nick)) : "");
}
});
XmppRoom = (function() {
function XmppRoom(client, name, nick, password) {
this.roster = {};
this._message_handlers = {};
this._presence_handlers = {};
this._roster_handlers = {};
this._handler_ids = 0;
this.client = client;
this.name = name;
this.nick = nick;
this.password = password;
this._roomRosterHandler = __bind(this._roomRosterHandler, this);
this._addOccupant = __bind(this._addOccupant, this);
if (client.muc) { this.client = client.muc; }
this.name = Strophe.getBareJidFromJid(name);
this.client.rooms[this.name] = this;
this.addHandler('presence', this._roomRosterHandler);
}
XmppRoom.prototype.join = function(msg_handler_cb, pres_handler_cb) {
if (!this.client.rooms[this.name]) {
return this.client.join(this.name, this.nick, msg_handler_cb, pres_handler_cb, this.password);
}
};
XmppRoom.prototype.leave = function(handler_cb, message) {
this.client.leave(this.name, this.nick, handler_cb, message);
return delete this.client.rooms[this.name];
};
XmppRoom.prototype.message = function(nick, message, html_message, type) {
return this.client.message(this.name, nick, message, html_message, type);
};
XmppRoom.prototype.groupchat = function(message, html_message) {
return this.client.groupchat(this.name, message, html_message);
};
XmppRoom.prototype.invite = function(receiver, reason) {
return this.client.invite(this.name, receiver, reason);
};
XmppRoom.prototype.directInvite = function(receiver, reason) {
return this.client.directInvite(this.name, receiver, reason, this.password);
};
XmppRoom.prototype.configure = function(handler_cb) {
return this.client.configure(this.name, handler_cb);
};
XmppRoom.prototype.cancelConfigure = function() {
return this.client.cancelConfigure(this.name);
};
XmppRoom.prototype.saveConfiguration = function(configarray) {
return this.client.saveConfiguration(this.name, configarray);
};
XmppRoom.prototype.queryOccupants = function(success_cb, error_cb) {
return this.client.queryOccupants(this.name, success_cb, error_cb);
};
XmppRoom.prototype.setTopic = function(topic) {
return this.client.setTopic(this.name, topic);
};
XmppRoom.prototype.modifyRole = function(nick, role, reason, success_cb, error_cb) {
return this.client.modifyRole(this.name, nick, role, reason, success_cb, error_cb);
};
XmppRoom.prototype.kick = function(nick, reason, handler_cb, error_cb) {
return this.client.kick(this.name, nick, reason, handler_cb, error_cb);
};
XmppRoom.prototype.voice = function(nick, reason, handler_cb, error_cb) {
return this.client.voice(this.name, nick, reason, handler_cb, error_cb);
};
XmppRoom.prototype.mute = function(nick, reason, handler_cb, error_cb) {
return this.client.mute(this.name, nick, reason, handler_cb, error_cb);
};
XmppRoom.prototype.op = function(nick, reason, handler_cb, error_cb) {
return this.client.op(this.name, nick, reason, handler_cb, error_cb);
};
XmppRoom.prototype.deop = function(nick, reason, handler_cb, error_cb) {
return this.client.deop(this.name, nick, reason, handler_cb, error_cb);
};
XmppRoom.prototype.modifyAffiliation = function(jid, affiliation, reason, success_cb, error_cb) {
return this.client.modifyAffiliation(this.name, jid, affiliation, reason, success_cb, error_cb);
};
XmppRoom.prototype.ban = function(jid, reason, handler_cb, error_cb) {
return this.client.ban(this.name, jid, reason, handler_cb, error_cb);
};
XmppRoom.prototype.member = function(jid, reason, handler_cb, error_cb) {
return this.client.member(this.name, jid, reason, handler_cb, error_cb);
};
XmppRoom.prototype.revoke = function(jid, reason, handler_cb, error_cb) {
return this.client.revoke(this.name, jid, reason, handler_cb, error_cb);
};
XmppRoom.prototype.owner = function(jid, reason, handler_cb, error_cb) {
return this.client.owner(this.name, jid, reason, handler_cb, error_cb);
};
XmppRoom.prototype.admin = function(jid, reason, handler_cb, error_cb) {
return this.client.admin(this.name, jid, reason, handler_cb, error_cb);
};
XmppRoom.prototype.changeNick = function(nick) {
this.nick = nick;
return this.client.changeNick(this.name, nick);
};
XmppRoom.prototype.setStatus = function(show, status) {
return this.client.setStatus(this.name, this.nick, show, status);
};
XmppRoom.prototype.addHandler = function(handler_type, handler) {
/* Adds a handler to the MUC room.
* Parameters:
* (String) handler_type - 'message', 'presence' or 'roster'.
* (Function) handler - The handler function.
* Returns:
* id - the id of handler.
*/
var id;
id = this._handler_ids++;
switch (handler_type) {
case 'presence':
this._presence_handlers[id] = handler;
break;
case 'message':
this._message_handlers[id] = handler;
break;
case 'roster':
this._roster_handlers[id] = handler;
break;
default:
this._handler_ids--;
return null;
}
return id;
};
XmppRoom.prototype.removeHandler = function(id) {
/* Removes a handler from the MUC room.
* This function takes ONLY ids returned by the addHandler function
* of this room. passing handler ids returned by connection.addHandler
* may brake things!
* Parameters:
* (number) id - the id of the handler
*/
delete this._presence_handlers[id];
delete this._message_handlers[id];
return delete this._roster_handlers[id];
};
XmppRoom.prototype._addOccupant = function(data) {
/* Creates and adds an Occupant to the Room Roster.
* Parameters:
* (Object) data - the data the Occupant is filled with
* Returns:
* occ - the created Occupant.
*/
var occ;
occ = new Occupant(data, this);
this.roster[occ.nick] = occ;
return occ;
};
XmppRoom.prototype._roomRosterHandler = function(pres) {
/* The standard handler that managed the Room Roster.
* Parameters:
* (Object) pres - the presence stanza containing user information
*/
var data, handler, id, newnick, nick, _ref;
data = XmppRoom._parsePresence(pres);
nick = data.nick;
newnick = data.newnick || null;
switch (data.type) {
case 'error':
return;
case 'unavailable':
if (newnick) {
data.nick = newnick;
if (this.roster[nick] && this.roster[newnick]) {
this.roster[nick].update(this.roster[newnick]);
this.roster[newnick] = this.roster[nick];
}
if (this.roster[nick] && !this.roster[newnick]) {
this.roster[newnick] = this.roster[nick].update(data);
}
}
delete this.roster[nick];
break;
default:
if (this.roster[nick]) {
this.roster[nick].update(data);
} else {
this._addOccupant(data);
}
}
_ref = this._roster_handlers;
for (id in _ref) {
handler = _ref[id];
if (!handler(this.roster, this)) { delete this._roster_handlers[id]; }
}
return true;
};
XmppRoom._parsePresence = function(pres) {
/* Parses a presence stanza into a map
* Parameters:
* (Object) pres - the presence stanza
* Returns:
* (Object) data - the data extracted from the presence stanza
*/
var $pres, data, i, j, children, item;
$pres = $(pres);
data = {};
data.nick = Strophe.getResourceFromJid($pres.attr('from'));
data.type = $pres.attr('type');
data.states = [];
for (i=0; i < $pres.children().length; i++) {
child = $pres.children()[0];
switch (child.nodeName) {
case 'status':
data.status = child.textContent || null;
break;
case 'show':
data.show = child.textContent || null;
break;
case 'x':
if ($(child).attr('xmlns') === Strophe.NS.MUC_USER) {
children = $(child).children();
for (j=0; j < children.length; j++) {
item = children[0];
switch (item.nodeName) {
case "item":
a = item.attributes;
data.affiliation = $(item).attr('affiliation') || null;
data.role = $(item).attr('role') || null;
data.jid = $(item).attr('jid') || null;
data.newnick = $(item).attr('nick') || null;
break;
case "status":
if ($(item).attr('code')) {
data.states.push($(item).attr('code'));
}
break;
}
}
}
break;
}
}
return data;
};
return XmppRoom;
})();
RoomConfig = (function() {
function RoomConfig(info) {
this.parse = __bind(this.parse, this);
if (info !== null) { this.parse(info); }
}
RoomConfig.prototype.parse = function(result) {
var attr, attrs, child, field, identity, query, _i, _j, _k, _len, _len2, _len3, _ref;
query = result.getElementsByTagName("query")[0].childNodes;
this.identities = [];
this.features = [];
this.x = [];
for (_i = 0, _len = query.length; _i < _len; _i++) {
child = query[_i];
attrs = child.attributes;
switch (child.nodeName) {
case "identity":
identity = {};
for (_j = 0, _len2 = attrs.length; _j < _len2; _j++) {
attr = attrs[_j];
identity[attr.name] = attr.textContent;
}
this.identities.push(identity);
break;
case "feature":
this.features.push(attrs["var"].textContent);
break;
case "x":
attrs = child.childNodes[0].attributes;
if ((!attrs["var"].textContent === 'FORM_TYPE') || (!attrs.type.textContent === 'hidden')) {
break;
}
_ref = child.childNodes;
for (_k = 0, _len3 = _ref.length; _k < _len3; _k++) {
field = _ref[_k];
if (!(!field.attributes.type)) continue;
attrs = field.attributes;
this.x.push({
"var": attrs["var"].textContent,
label: attrs.label.textContent || "",
value: field.firstChild.textContent || ""
});
}
}
}
return {
"identities": this.identities,
"features": this.features,
"x": this.x
};
};
return RoomConfig;
})();
Occupant = (function() {
function Occupant(data, room) {
this.room = room;
this.update = __bind(this.update, this);
this.admin = __bind(this.admin, this);
this.owner = __bind(this.owner, this);
this.revoke = __bind(this.revoke, this);
this.member = __bind(this.member, this);
this.ban = __bind(this.ban, this);
this.modifyAffiliation = __bind(this.modifyAffiliation, this);
this.deop = __bind(this.deop, this);
this.op = __bind(this.op, this);
this.mute = __bind(this.mute, this);
this.voice = __bind(this.voice, this);
this.kick = __bind(this.kick, this);
this.modifyRole = __bind(this.modifyRole, this);
this.update(data);
}
Occupant.prototype.modifyRole = function(role, reason, success_cb, error_cb) {
return this.room.modifyRole(this.nick, role, reason, success_cb, error_cb);
};
Occupant.prototype.kick = function(reason, handler_cb, error_cb) {
return this.room.kick(this.nick, reason, handler_cb, error_cb);
};
Occupant.prototype.voice = function(reason, handler_cb, error_cb) {
return this.room.voice(this.nick, reason, handler_cb, error_cb);
};
Occupant.prototype.mute = function(reason, handler_cb, error_cb) {
return this.room.mute(this.nick, reason, handler_cb, error_cb);
};
Occupant.prototype.op = function(reason, handler_cb, error_cb) {
return this.room.op(this.nick, reason, handler_cb, error_cb);
};
Occupant.prototype.deop = function(reason, handler_cb, error_cb) {
return this.room.deop(this.nick, reason, handler_cb, error_cb);
};
Occupant.prototype.modifyAffiliation = function(affiliation, reason, success_cb, error_cb) {
return this.room.modifyAffiliation(this.jid, affiliation, reason, success_cb, error_cb);
};
Occupant.prototype.ban = function(reason, handler_cb, error_cb) {
return this.room.ban(this.jid, reason, handler_cb, error_cb);
};
Occupant.prototype.member = function(reason, handler_cb, error_cb) {
return this.room.member(this.jid, reason, handler_cb, error_cb);
};
Occupant.prototype.revoke = function(reason, handler_cb, error_cb) {
return this.room.revoke(this.jid, reason, handler_cb, error_cb);
};
Occupant.prototype.owner = function(reason, handler_cb, error_cb) {
return this.room.owner(this.jid, reason, handler_cb, error_cb);
};
Occupant.prototype.admin = function(reason, handler_cb, error_cb) {
return this.room.admin(this.jid, reason, handler_cb, error_cb);
};
Occupant.prototype.update = function(data) {
this.nick = data.nick || null;
this.affiliation = data.affiliation || null;
this.role = data.role || null;
this.jid = data.jid || null;
this.status = data.status || null;
this.show = data.show || null;
return this;
};
return Occupant;
})();
}).call(this);
}));

View File

@ -1,443 +0,0 @@
/*
Copyright 2010, François de Metz <francois@2metz.fr>
*/
/**
* Roster Plugin
* Allow easily roster management
*
* Features
* * Get roster from server
* * handle presence
* * handle roster iq
* * subscribe/unsubscribe
* * authorize/unauthorize
* * roster versioning (xep 237)
*/
Strophe.addConnectionPlugin('roster',
{
_connection: null,
_callbacks : [],
/** Property: items
* Roster items
* [
* {
* name : "",
* jid : "",
* subscription : "",
* ask : "",
* groups : ["", ""],
* resources : {
* myresource : {
* show : "",
* status : "",
* priority : ""
* }
* }
* }
* ]
*/
items : [],
/** Property: ver
* current roster revision
* always null if server doesn't support xep 237
*/
ver : null,
/** Function: init
* Plugin init
*
* Parameters:
* (Strophe.Connection) conn - Strophe connection
*/
init: function(conn)
{
this._connection = conn;
this.items = [];
// Override the connect and attach methods to always add presence and roster handlers.
// They are removed when the connection disconnects, so must be added on connection.
var oldCallback, roster = this, _connect = conn.connect, _attach = conn.attach;
var newCallback = function(status)
{
if (status == Strophe.Status.ATTACHED || status == Strophe.Status.CONNECTED)
{
try
{
// Presence subscription
conn.addHandler(roster._onReceivePresence.bind(roster), null, 'presence', null, null, null);
conn.addHandler(roster._onReceiveIQ.bind(roster), Strophe.NS.ROSTER, 'iq', "set", null, null);
}
catch (e)
{
Strophe.error(e);
}
}
if (oldCallback !== null)
oldCallback.apply(this, arguments);
};
conn.connect = function(jid, pass, callback, wait, hold)
{
oldCallback = callback;
if (typeof arguments[0] == "undefined")
arguments[0] = null;
if (typeof arguments[1] == "undefined")
arguments[1] = null;
arguments[2] = newCallback;
_connect.apply(conn, arguments);
};
conn.attach = function(jid, sid, rid, callback, wait, hold, wind)
{
oldCallback = callback;
if (typeof arguments[0] == "undefined")
arguments[0] = null;
if (typeof arguments[1] == "undefined")
arguments[1] = null;
if (typeof arguments[2] == "undefined")
arguments[2] = null;
arguments[3] = newCallback;
_attach.apply(conn, arguments);
};
Strophe.addNamespace('ROSTER_VER', 'urn:xmpp:features:rosterver');
Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
},
/** Function: supportVersioning
* return true if roster versioning is enabled on server
*/
supportVersioning: function()
{
return (this._connection.features && this._connection.features.getElementsByTagName('ver').length > 0);
},
/** Function: get
* Get Roster on server
*
* Parameters:
* (Function) userCallback - callback on roster result
* (String) ver - current rev of roster
* (only used if roster versioning is enabled)
* (Array) items - initial items of ver
* (only used if roster versioning is enabled)
* In browser context you can use sessionStorage
* to store your roster in json (JSON.stringify())
*/
get: function(userCallback, ver, items)
{
var attrs = {xmlns: Strophe.NS.ROSTER};
this.items = [];
if (this.supportVersioning())
{
// empty rev because i want an rev attribute in the result
attrs.ver = ver || '';
this.items = items || [];
}
var iq = $iq({type: 'get', 'id' : this._connection.getUniqueId('roster')}).c('query', attrs);
return this._connection.sendIQ(iq,
this._onReceiveRosterSuccess.bind(this, userCallback),
this._onReceiveRosterError.bind(this, userCallback));
},
/** Function: registerCallback
* register callback on roster (presence and iq)
*
* Parameters:
* (Function) call_back
*/
registerCallback: function(call_back)
{
this._callbacks.push(call_back);
},
/** Function: findItem
* Find item by JID
*
* Parameters:
* (String) jid
*/
findItem : function(jid)
{
for (var i = 0; i < this.items.length; i++)
{
if (this.items[i] && this.items[i].jid == jid)
{
return this.items[i];
}
}
return false;
},
/** Function: removeItem
* Remove item by JID
*
* Parameters:
* (String) jid
*/
removeItem : function(jid)
{
for (var i = 0; i < this.items.length; i++)
{
if (this.items[i] && this.items[i].jid == jid)
{
this.items.splice(i, 1);
return true;
}
}
return false;
},
/** Function: subscribe
* Subscribe presence
*
* Parameters:
* (String) jid
* (String) message (optional)
* (String) nick (optional)
*/
subscribe: function(jid, message, nick) {
var pres = $pres({to: jid, type: "subscribe"});
if (message && message !== "") {
pres.c("status").t(message);
}
if (nick && nick !== "") {
pres.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick);
}
this._connection.send(pres);
},
/** Function: unsubscribe
* Unsubscribe presence
*
* Parameters:
* (String) jid
* (String) message
*/
unsubscribe: function(jid, message)
{
var pres = $pres({to: jid, type: "unsubscribe"});
if (message && message != "")
pres.c("status").t(message);
this._connection.send(pres);
},
/** Function: authorize
* Authorize presence subscription
*
* Parameters:
* (String) jid
* (String) message
*/
authorize: function(jid, message)
{
var pres = $pres({to: jid, type: "subscribed"});
if (message && message != "")
pres.c("status").t(message);
this._connection.send(pres);
},
/** Function: unauthorize
* Unauthorize presence subscription
*
* Parameters:
* (String) jid
* (String) message
*/
unauthorize: function(jid, message)
{
var pres = $pres({to: jid, type: "unsubscribed"});
if (message && message != "")
pres.c("status").t(message);
this._connection.send(pres);
},
/** Function: add
* Add roster item
*
* Parameters:
* (String) jid - item jid
* (String) name - name
* (Array) groups
* (Function) call_back
*/
add: function(jid, name, groups, call_back)
{
var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: jid,
name: name});
for (var i = 0; i < groups.length; i++)
{
iq.c('group').t(groups[i]).up();
}
this._connection.sendIQ(iq, call_back, call_back);
},
/** Function: update
* Update roster item
*
* Parameters:
* (String) jid - item jid
* (String) name - name
* (Array) groups
* (Function) call_back
*/
update: function(jid, name, groups, call_back)
{
var item = this.findItem(jid);
if (!item)
{
throw "item not found";
}
var newName = name || item.name;
var newGroups = groups || item.groups;
var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
name: newName});
for (var i = 0; i < newGroups.length; i++)
{
iq.c('group').t(newGroups[i]).up();
}
return this._connection.sendIQ(iq, call_back, call_back);
},
/** Function: remove
* Remove roster item
*
* Parameters:
* (String) jid - item jid
* (Function) call_back
*/
remove: function(jid, call_back)
{
var item = this.findItem(jid);
if (!item)
{
throw "item not found";
}
var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
subscription: "remove"});
this._connection.sendIQ(iq, call_back, call_back);
},
/** PrivateFunction: _onReceiveRosterSuccess
*
*/
_onReceiveRosterSuccess: function(userCallback, stanza)
{
this._updateItems(stanza);
userCallback(this.items);
},
/** PrivateFunction: _onReceiveRosterError
*
*/
_onReceiveRosterError: function(userCallback, stanza)
{
userCallback(this.items);
},
/** PrivateFunction: _onReceivePresence
* Handle presence
*/
_onReceivePresence : function(presence)
{
// TODO: from is optional
var jid = presence.getAttribute('from');
var from = Strophe.getBareJidFromJid(jid);
var item = this.findItem(from);
// not in roster
if (!item)
{
return true;
}
var type = presence.getAttribute('type');
if (type == 'unavailable')
{
delete item.resources[Strophe.getResourceFromJid(jid)];
}
else if (!type)
{
// TODO: add timestamp
item.resources[Strophe.getResourceFromJid(jid)] = {
show : (presence.getElementsByTagName('show').length != 0) ? Strophe.getText(presence.getElementsByTagName('show')[0]) : "",
status : (presence.getElementsByTagName('status').length != 0) ? Strophe.getText(presence.getElementsByTagName('status')[0]) : "",
priority : (presence.getElementsByTagName('priority').length != 0) ? Strophe.getText(presence.getElementsByTagName('priority')[0]) : ""
};
}
else
{
// Stanza is not a presence notification. (It's probably a subscription type stanza.)
return true;
}
this._call_backs(this.items, item);
return true;
},
/** PrivateFunction: _call_backs
*
*/
_call_backs : function(items, item)
{
for (var i = 0; i < this._callbacks.length; i++) // [].forEach my love ...
{
this._callbacks[i](items, item);
}
},
/** PrivateFunction: _onReceiveIQ
* Handle roster push.
*/
_onReceiveIQ : function(iq)
{
var id = iq.getAttribute('id');
var from = iq.getAttribute('from');
// Receiving client MUST ignore stanza unless it has no from or from = user's JID.
if (from && from != "" && from != this._connection.jid && from != Strophe.getBareJidFromJid(this._connection.jid))
return true;
var iqresult = $iq({type: 'result', id: id, from: this._connection.jid});
this._connection.send(iqresult);
this._updateItems(iq);
return true;
},
/** PrivateFunction: _updateItems
* Update items from iq
*/
_updateItems : function(iq)
{
var query = iq.getElementsByTagName('query');
if (query.length != 0)
{
this.ver = query.item(0).getAttribute('ver');
var self = this;
Strophe.forEachChild(query.item(0), 'item',
function (item)
{
self._updateItem(item);
}
);
}
this._call_backs(this.items);
},
/** PrivateFunction: _updateItem
* Update internal representation of roster item
*/
_updateItem : function(item)
{
var jid = item.getAttribute("jid");
var name = item.getAttribute("name");
var subscription = item.getAttribute("subscription");
var ask = item.getAttribute("ask");
var groups = [];
Strophe.forEachChild(item, 'group',
function(group)
{
groups.push(Strophe.getText(group));
}
);
if (subscription == "remove")
{
this.removeItem(jid);
return;
}
var item = this.findItem(jid);
if (!item)
{
this.items.push({
name : name,
jid : jid,
subscription : subscription,
ask : ask,
groups : groups,
resources : {}
});
}
else
{
item.name = name;
item.subscription = subscription;
item.ask = ask;
item.groups = groups;
}
}
});

View File

@ -1,66 +0,0 @@
// Generated by CoffeeScript 1.3.3
/*
Plugin to implement the vCard extension.
http://xmpp.org/extensions/xep-0054.html
Author: Nathan Zorn (nathan.zorn@gmail.com)
CoffeeScript port: Andreas Guth (guth@dbis.rwth-aachen.de)
*/
/* jslint configuration:
*/
/* global document, window, setTimeout, clearTimeout, console,
XMLHttpRequest, ActiveXObject,
Base64, MD5,
Strophe, $build, $msg, $iq, $pres
*/
var buildIq;
buildIq = function(type, jid, vCardEl) {
var iq;
iq = $iq(jid ? {
type: type,
to: jid
} : {
type: type
});
iq.c("vCard", {
xmlns: Strophe.NS.VCARD
});
if (vCardEl) {
iq.cnode(vCardEl);
}
return iq;
};
Strophe.addConnectionPlugin('vcard', {
_connection: null,
init: function(conn) {
this._connection = conn;
return Strophe.addNamespace('VCARD', 'vcard-temp');
},
/*Function
Retrieve a vCard for a JID/Entity
Parameters:
(Function) handler_cb - The callback function used to handle the request.
(String) jid - optional - The name of the entity to request the vCard
If no jid is given, this function retrieves the current user's vcard.
*/
get: function(handler_cb, jid, error_cb) {
var iq;
iq = buildIq("get", jid);
return this._connection.sendIQ(iq, handler_cb, error_cb);
},
/* Function
Set an entity's vCard.
*/
set: function(handler_cb, vCardEl, jid, error_cb) {
var iq;
iq = buildIq("set", jid, vCardEl);
return this._connection.sendIQ(iq, handler_cb, error_rb);
}
});

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,11 @@ converse.js
Converse.js_ is a web based `XMPP/Jabber`_ instant messaging client.
It is used by collective.xmpp.chat_, which is a Plone_ instant messaging add-on.
It enables you to add chat functionality to your website, independent of any
specific backend. You will however need an XMPP server to connect to, either
your own, or a public one.
The ultimate goal is to enable anyone to add chat functionality to their websites, independent of any backend.
You will however need an XMPP server to connect to, either your own, or a public one.
It is used by collective.xmpp.chat_, which is a Plone_ instant messaging add-on.
--------
Features
@ -15,15 +16,19 @@ Features
It has the following features:
* Manually or automically subscribe to other users.
* Single-user chat
* Multi-user chat in chatrooms
* vCard support
* Service discovery
* Contact rosters
* Manually or automically subscribe to other contacts
* Accept or decline contact requests
* Chat status (online, busy, away, offline)
* Roster item exchange
* Chat statuses (online, busy, away, offline)
* Custom status messages
* Typing notifications
* Third person messages (/me )
* Multi-user chat in chatrooms
* Chatroom Topics
* vCard support
* Translated into multiple languages (af, de, es, it, pt_BR)
-----------
Screencasts
@ -32,15 +37,39 @@ Screencasts
* `In a static HTML page`_. Here we chat to external XMPP accounts on Jabber.org and Gmail.
* `Integrated into a Plone site`_ via collective.xmpp.chat.
----
Demo
----
A live demo is available at `conversejs.org`_
-----
Tests
-----
We use behavior-driven tests written with jasmine.js_. They can run in your
browser (`see here`_) or in the command line via phantom.js_.
We use `Travis-CI`_ for continuous integration.
.. image:: https://api.travis-ci.org/jcbrand/converse.js.png?branch=master
-------------
Documentation
-------------
The developer/integrator documentation can be found at `<http://conversejs.org/docs/html>`_.
------------
Dependencies
------------
It depends on quite a few third party libraries, including:
* jquery_
* strophe.js_
* backbone.js_
* require.js_
-------
Licence
@ -61,3 +90,9 @@ Licence
.. _Screencast2: http://opkode.com/media/blog/2013/04/02/converse.js-xmpp-instant-messaging-with-javascript
.. _`Integrated into a Plone site`: http://opkode.com/media/blog/instant-messaging-for-plone-with-javascript-and-xmpp
.. _`In a static HTML page`: http://opkode.com/media/blog/2013/04/02/converse.js-xmpp-instant-messaging-with-javascript
.. _`conversejs.org`: http://conversejs.org
.. _jquery: http://jquery.com
.. _jasmine.js: http://pivotal.github.io/jasmine
.. _`see here`: http://conversejs.org/tests.html
.. _phantom.js: http://phantomjs.org
.. _`Travis-CI`: https://travis-ci.org

24
bower.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "converse",
"version": "0.5.0",
"devDependencies": {
"jasmine": "https://github.com/jcbrand/jasmine.git#1_3_x"
},
"dependencies": {
"requirejs": "2.1.8",
"jquery": "1.8.3",
"sjcl": "git://github.com/bitwiseshiftleft/sjcl.git",
"jed": "0.5.4",
"tinysort": "git://github.com/Sjeiti/TinySort.git",
"underscore": "1.5.1",
"backbone": "1.0.0",
"backbone.localStorage": "1.1.6",
"strophe": "git://github.com/strophe/strophejs.git#8e14efdf01856d184f6ba46b3b82c888beacdd98",
"strophe.roster": "https://raw.github.com/jcbrand/strophejs-plugins/75c8693992bc357c699b6d615eeb396e799f5c02/roster/strophe.roster.js",
"strophe.vcard": "https://raw.github.com/jcbrand/strophejs-plugins/75c8693992bc357c699b6d615eeb396e799f5c02/vcard/strophe.vcard.js",
"strophe.disco": "https://raw.github.com/jcbrand/strophejs-plugins/75c8693992bc357c699b6d615eeb396e799f5c02/disco/strophe.disco.js",
"strophe.muc": "https://raw.github.com/jcbrand/strophejs-plugins/75c8693992bc357c699b6d615eeb396e799f5c02/muc/strophe.muc.js"
},
"exportsOverride": {
}
}

View File

@ -1,27 +0,0 @@
({
baseUrl: ".",
paths: {
"jquery": "Libraries/require-jquery",
"jed": "Libraries/jed",
"locales": "locale/locales",
"af": "locale/af/LC_MESSAGES/af",
"en": "locale/en/LC_MESSAGES/en",
"de": "locale/de/LC_MESSAGES/de",
"es": "locale/es/LC_MESSAGES/es",
"hu": "locale/hu/LC_MESSAGES/hu",
"it": "locale/it/LC_MESSAGES/it",
"pt_BR": "locale/pt_BR/LC_MESSAGES/pt_BR",
"sjcl": "Libraries/sjcl",
"tinysort": "Libraries/jquery.tinysort",
"underscore": "Libraries/underscore",
"backbone": "Libraries/backbone",
"localstorage": "Libraries/backbone.localStorage",
"strophe": "Libraries/strophe",
"strophe.muc": "Libraries/strophe.muc",
"strophe.roster": "Libraries/strophe.roster",
"strophe.vcard": "Libraries/strophe.vcard",
"strophe.disco": "Libraries/strophe.disco"
},
name: "main",
out: "converse.min.js"
})

12
converse-0.5.0.min.css vendored Normal file

File diff suppressed because one or more lines are too long

138
converse-0.5.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,11 @@
/*!
* Converse.js (Web-based XMPP instant messaging client)
* http://conversejs.org
*
* Copyright (c) 2012, Jan-Carel Brand <jc@opkode.com>
* Dual licensed under the MIT and GPL Licenses
*/
.hidden {
display: none
}

View File

@ -12,47 +12,12 @@
console = { log: function () {}, error: function () {} };
}
if (typeof define === 'function' && define.amd) {
require.config({
paths: {
"locales": "locale/locales",
"sjcl": "Libraries/sjcl",
"tinysort": "Libraries/jquery.tinysort",
"underscore": "Libraries/underscore",
"backbone": "Libraries/backbone",
"localstorage": "Libraries/backbone.localStorage",
"strophe": "Libraries/strophe",
"strophe.muc": "Libraries/strophe.muc",
"strophe.roster": "Libraries/strophe.roster",
"strophe.vcard": "Libraries/strophe.vcard",
"strophe.disco": "Libraries/strophe.disco"
},
// define module dependencies for modules not using define
shim: {
'backbone': {
//These script dependencies should be loaded before loading
//backbone.js
deps: [
'underscore',
'jquery'
],
//Once loaded, use the global 'Backbone' as the
//module value.
exports: 'Backbone'
},
'underscore': { exports: '_' },
'strophe.muc': { deps: ['strophe', 'jquery'] },
'strophe.roster': { deps: ['strophe', 'jquery'] },
'strophe.vcard': { deps: ['strophe', 'jquery'] },
'strophe.disco': { deps: ['strophe', 'jquery'] }
}
});
define("converse", [
"locales",
"localstorage",
"tinysort",
"sjcl",
"strophe",
"strophe.muc",
"strophe.roster",
"strophe.vcard",
@ -1869,7 +1834,7 @@
}
callback(jid, fullname, img, img_type, url);
}, this), jid, errback);
}
};
this.RosterItems = Backbone.Collection.extend({
model: converse.RosterItem,
@ -2350,7 +2315,7 @@
setStatusMessage: function (ev) {
ev.preventDefault();
var status_message = $(ev.target).find('input').attr('value');
var status_message = $(ev.target).find('input').val();
if (status_message === "") {
}
this.model.setStatusMessage(status_message);

1
converse.min.css vendored

File diff suppressed because one or more lines are too long

140
converse.min.js vendored

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 11cf73c9901b429394dccc272608c52e
config: 412a90a1e0621af20e495234fc711bdf
tags: fbb0d17656682115ca4d033fb2f83ba1

View File

@ -10,6 +10,60 @@
:depth: 3
:local:
=========================================
Quickstart (to get a demo up and running)
=========================================
When you download a specific release of *Converse.js* there will be two minified files inside the zip file.
* converse.min.js
* converse.min.css
You can include these two files inside the *<head>* element of your website via the *script* and *link*
tags:
::
<link rel="stylesheet" type="text/css" media="screen" href="converse.min.css">
<script src="converse.min.js"></script>
Then, at the bottom of your page, after the closing *</body>* element, put the
following inline Javascript code:
::
require(['converse'], function (converse) {
converse.initialize({
auto_list_rooms: false,
auto_subscribe: false,
bosh_service_url: 'https://bind.opkode.im', // Please use this connection manager only for testing purposes
hide_muc_server: false,
i18n: locales.en, // Refer to ./locale/locales.js to see which locales are supported
prebind: false,
show_controlbox_by_default: true,
xhr_user_search: false
});
});
The *index.html* file inside the Converse.js folder serves as a nice usable
example of this.
These minified files provide the same demo-like functionality as is available
on the `conversejs.org`_ website. Useful for testing or demoing, but not very
practical.
You'll most likely want to implement some kind of single-signon solution for
your website, where users authenticate once in your website and then stay
logged into their XMPP session upon page reload.
For more info on this, read: `Pre-binding and Single Session Support`_.
You might also want to have more fine-grained control of what gets included in
the minified Javascript file. Read `Configuration`_ and `Minification`_ for more info on how to do
that.
============
Introduction
============
@ -36,6 +90,7 @@ code.
The `What you will need`_ section has more information on all these
requirements.
==================
What you will need
==================
@ -151,85 +206,109 @@ In the callback function, you call *converse.onConnected* together with the
connection object.
=========================================
Quickstart (to get a demo up and running)
=========================================
When you download a specific release of *Converse.js* there will be two minified files inside the zip file.
* converse.min.js
* converse.min.css
You can include these two files inside the *<head>* element of your website via the *script* and *link*
tags:
::
<link rel="stylesheet" type="text/css" media="screen" href="converse.min.css">
<script src="converse.min.js"></script>
Then, at the bottom of your page, after the closing *</body>* element, put the
following inline Javascript code:
::
require(['converse'], function (converse) {
converse.initialize({
auto_list_rooms: false,
auto_subscribe: false,
bosh_service_url: 'https://bind.opkode.im', // Please use this connection manager only for testing purposes
hide_muc_server: false,
i18n: locales.en, // Refer to ./locale/locales.js to see which locales are supported
prebind: false,
show_controlbox_by_default: true,
xhr_user_search: false
});
});
The *index.html* file inside the Converse.js folder serves as a nice usable
example of this.
These minified files provide the same demo-like functionality as is available
on the `conversejs.org`_ website. Useful for testing or demoing, but not very
practical.
You'll most likely want to implement some kind of single-signon solution for
your website, where users authenticate once in your website and then stay
logged into their XMPP session upon page reload.
For more info on this, read: `Pre-binding and Single Session Support`_.
You might also want to have more fine-grained control of what gets included in
the minified Javascript file. Read `Configuration`_ and `Minification`_ for more info on how to do
that.
===========
Development
===========
With AMD and require.js (recommended)
-------------------------------------
Install Node.js and development dependencies
============================================
Converse.js uses `require.js`_ to track and load dependencies.
We use development tools (`Grunt <http://gruntjs.com>`_ and `Bower <http://bower.io>`_)
which depend on Node.js and npm (the Node package manager).
If you don't have Node.js installed, you can download and install the latest
version `here <https://nodejs.org/download>`_.
Once you have Node.js installed, run the following command in the Converse.js
directory:
::
npm install
Install 3rd party dependencies
==============================
Now that we have Grunt and Bower, you can install and configure Converse's
3rd party dependencies with the following command:
::
grunt fetch
With AMD and require.js (recommended)
=====================================
Converse.js uses `require.js`_ to asynchronously load dependencies.
If you want to develop or customize converse.js, you'll want to load the
non-minified javascript files.
Add the following two lines to the *<head>* section of your webpage.
Add the following two lines to the *<head>* section of your webpage:
::
<link rel="stylesheet" type="text/css" media="screen" href="converse.css">
<script data-main="main" src="Libraries/require-jquery.js"></script>
<script data-main="main" src="components/requirejs/require.js"></script>
require.js will then let the main.js file be parsed (because of the *data-main*
attribute on the *script* tag), which will in turn cause converse.js to be
parsed.
Without AMD and require.js
--------------------------
==========================
Converse.js can also be used without require.js. If you for some reason prefer
to use it this way, please refer to *non_amd.html* for an example of how and in
what order all the Javascript files that converse.js depends on need to be
loaded.
to use it this way, please refer to
`non_amd.html <https://github.com/jcbrand/converse.js/blob/master/non_amd.html>`_
for an example of how and in what order all the Javascript files that converse.js
depends on need to be loaded.
Before submitting a pull request
================================
Add tests for your bugfix or feature
------------------------------------
Add a test for any bug fixed or feature added. We use Jasmine
for testing.
Take a look at ``tests.html`` and ``spec/MainSpec.js`` to see how
the tests are implemented.
If you are unsure how to write tests, please
`contact me <http://conversejs.org>`_ and I'll be happy to help.
Check that the tests pass
-------------------------
Check that the Jasmine tests complete sucessfully. Open
`tests.html <https://github.com/jcbrand/converse.js/blob/master/tests.html>`_
in your browser, and the tests will run automatically.
On the command line you can run:
::
grunt test
Check your code for errors or bad habits by running JSHint
----------------------------------------------------------
`JSHint <http://jshint.com>`_ will do a static analysis of your code and hightlight potential errors
and/or bad habits.
::
grunt jshint
You can run both the tests and jshint in one go by calling:
::
grunt check
=============
@ -290,7 +369,7 @@ bosh_service_url
Connections to an XMPP server depend on a BOSH connection manager which acts as
a middle man between HTTP and XMPP.
See `here`_ for more information.
See `here <http://metajack.im/2008/09/08/which-bosh-server-do-you-need>`_ for more information.
fullname
--------
@ -505,7 +584,6 @@ those hoops you had to jump through.
.. _`conversejs.org`: http://conversejs.org
.. _`require.js`: http://requirejs.org
.. _`read more about require.js's optimizer here`: http://requirejs.org/docs/optimization.html
.. _`here`: http://metajack.im/2008/09/08/which-bosh-server-do-you-need/l
.. _`HTTP`: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
.. _`XMPP`: https://en.wikipedia.org/wiki/Xmpp
.. _`Converse.js homepage`: http://conversejs.org

View File

@ -9,7 +9,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Index &mdash; Converse.js 0.3 documentation</title>
<title>Index &mdash; Converse.js 0.5 documentation</title>
<link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
@ -17,7 +17,7 @@
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '',
VERSION: '0.3',
VERSION: '0.5',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
@ -26,7 +26,7 @@
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="Converse.js 0.3 documentation" href="index.html" />
<link rel="top" title="Converse.js 0.5 documentation" href="index.html" />
</head>
<body>
<div id="header_wrap" class="outer">
@ -51,7 +51,7 @@
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
accesskey="I">index</a></li>
<li><a href="index.html">Converse.js 0.3 documentation</a> &raquo;</li>
<li><a href="index.html">Converse.js 0.5 documentation</a> &raquo;</li>
</ul>
</div>
<section id="main_content" class="inner">
@ -80,7 +80,7 @@
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
>index</a></li>
<li><a href="index.html">Converse.js 0.3 documentation</a> &raquo;</li>
<li><a href="index.html">Converse.js 0.5 documentation</a> &raquo;</li>
</ul>
</div>
</div>

View File

@ -7,7 +7,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Introduction &mdash; Converse.js 0.3 documentation</title>
<title>Quickstart (to get a demo up and running) &mdash; Converse.js 0.5 documentation</title>
<link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
@ -15,7 +15,7 @@
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '',
VERSION: '0.3',
VERSION: '0.5',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
@ -24,7 +24,7 @@
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="Converse.js 0.3 documentation" href="#" />
<link rel="top" title="Converse.js 0.5 documentation" href="#" />
</head>
<body>
<div id="header_wrap" class="outer">
@ -49,7 +49,7 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li><a href="#">Converse.js 0.3 documentation</a> &raquo;</li>
<li><a href="#">Converse.js 0.5 documentation</a> &raquo;</li>
</ul>
</div>
<section id="main_content" class="inner">
@ -65,151 +65,58 @@
<div class="contents local topic" id="table-of-contents">
<p class="topic-title first">Table of Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#introduction" id="id1">Introduction</a></li>
<li><a class="reference internal" href="#what-you-will-need" id="id2">What you will need</a><ul>
<li><a class="reference internal" href="#an-xmpp-jabber-server" id="id3">An XMPP/Jabber server</a></li>
<li><a class="reference internal" href="#connection-manager" id="id4">Connection Manager</a><ul>
<li><a class="reference internal" href="#overcoming-cross-domain-request-restrictions" id="id5">Overcoming cross-domain request restrictions</a></li>
<li><a class="reference internal" href="#quickstart-to-get-a-demo-up-and-running" id="id2">Quickstart (to get a demo up and running)</a></li>
<li><a class="reference internal" href="#introduction" id="id3">Introduction</a></li>
<li><a class="reference internal" href="#what-you-will-need" id="id4">What you will need</a><ul>
<li><a class="reference internal" href="#an-xmpp-jabber-server" id="id5">An XMPP/Jabber server</a></li>
<li><a class="reference internal" href="#connection-manager" id="id6">Connection Manager</a><ul>
<li><a class="reference internal" href="#overcoming-cross-domain-request-restrictions" id="id7">Overcoming cross-domain request restrictions</a></li>
</ul>
</li>
<li><a class="reference internal" href="#server-side-authentication" id="id6">Server-side authentication</a><ul>
<li><a class="reference internal" href="#pre-binding-and-single-session-support" id="id7">Pre-binding and Single Session Support</a></li>
<li><a class="reference internal" href="#server-side-authentication" id="id8">Server-side authentication</a><ul>
<li><a class="reference internal" href="#pre-binding-and-single-session-support" id="id9">Pre-binding and Single Session Support</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#quickstart-to-get-a-demo-up-and-running" id="id8">Quickstart (to get a demo up and running)</a></li>
<li><a class="reference internal" href="#development" id="id9">Development</a></li>
<li><a class="reference internal" href="#configuration" id="id10">Configuration</a><ul>
<li><a class="reference internal" href="#configuration-variables" id="id11">Configuration variables</a><ul>
<li><a class="reference internal" href="#animate" id="id12">animate</a></li>
<li><a class="reference internal" href="#auto-list-rooms" id="id13">auto_list_rooms</a></li>
<li><a class="reference internal" href="#auto-subscribe" id="id14">auto_subscribe</a></li>
<li><a class="reference internal" href="#bosh-service-url" id="id15">bosh_service_url</a></li>
<li><a class="reference internal" href="#fullname" id="id16">fullname</a></li>
<li><a class="reference internal" href="#hide-muc-server" id="id17">hide_muc_server</a></li>
<li><a class="reference internal" href="#prebind" id="id18">prebind</a></li>
<li><a class="reference internal" href="#show-controlbox-by-default" id="id19">show_controlbox_by_default</a></li>
<li><a class="reference internal" href="#xhr-user-search" id="id20">xhr_user_search</a></li>
<li><a class="reference internal" href="#development" id="id10">Development</a><ul>
<li><a class="reference internal" href="#install-node-js-and-development-dependencies" id="id11">Install Node.js and development dependencies</a></li>
<li><a class="reference internal" href="#install-3rd-party-dependencies" id="id12">Install 3rd party dependencies</a></li>
<li><a class="reference internal" href="#with-amd-and-require-js-recommended" id="id13">With AMD and require.js (recommended)</a></li>
<li><a class="reference internal" href="#without-amd-and-require-js" id="id14">Without AMD and require.js</a></li>
<li><a class="reference internal" href="#before-submitting-a-pull-request" id="id15">Before submitting a pull request</a><ul>
<li><a class="reference internal" href="#add-tests-for-your-bugfix-or-feature" id="id16">Add tests for your bugfix or feature</a></li>
<li><a class="reference internal" href="#check-that-the-tests-pass" id="id17">Check that the tests pass</a></li>
<li><a class="reference internal" href="#check-your-code-for-errors-or-bad-habits-by-running-jshint" id="id18">Check your code for errors or bad habits by running JSHint</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#minification" id="id21">Minification</a><ul>
<li><a class="reference internal" href="#minifying-javascript" id="id22">Minifying Javascript</a></li>
<li><a class="reference internal" href="#minifying-css" id="id23">Minifying CSS</a></li>
<li><a class="reference internal" href="#configuration" id="id19">Configuration</a><ul>
<li><a class="reference internal" href="#configuration-variables" id="id20">Configuration variables</a><ul>
<li><a class="reference internal" href="#animate" id="id21">animate</a></li>
<li><a class="reference internal" href="#auto-list-rooms" id="id22">auto_list_rooms</a></li>
<li><a class="reference internal" href="#auto-subscribe" id="id23">auto_subscribe</a></li>
<li><a class="reference internal" href="#bosh-service-url" id="id24">bosh_service_url</a></li>
<li><a class="reference internal" href="#fullname" id="id25">fullname</a></li>
<li><a class="reference internal" href="#hide-muc-server" id="id26">hide_muc_server</a></li>
<li><a class="reference internal" href="#prebind" id="id27">prebind</a></li>
<li><a class="reference internal" href="#show-controlbox-by-default" id="id28">show_controlbox_by_default</a></li>
<li><a class="reference internal" href="#xhr-user-search" id="id29">xhr_user_search</a></li>
</ul>
</li>
<li><a class="reference internal" href="#translations" id="id24">Translations</a></li>
</ul>
</div>
<div class="section" id="introduction">
<h1><a class="toc-backref" href="#id1">Introduction</a><a class="headerlink" href="#introduction" title="Permalink to this headline"></a></h1>
<p>Even though you can connect to public XMPP servers on the <a class="reference external" href="http://conversejs.org">conversejs.org</a>
website, <em>Converse.js</em> is not really meant to be a &#8220;Software-as-a-service&#8221; (SaaS)
webchat.</p>
<p>Instead, its goal is to provide the means for website owners to add a tightly
integrated instant messaging service to their own sites.</p>
<p>As a website owner, you are expected to host <em>Converse.js</em> yourself, and to do some legwork to
properly configure and integrate it into your site.</p>
<p>The benefit in doing this, is that your users have a much more streamlined and integrated
webchat experience and that you have control over the data. The latter being a
requirement for many sites dealing with sensitive information.</p>
<p>You&#8217;ll need to set up your own XMPP server and in order to have
<a class="reference internal" href="#session-support">Session Support</a> (i.e. single-signon functionality whereby users are authenticated once and stay
logged in to XMPP upon page reload) you will also have to add some server-side
code.</p>
<p>The <a class="reference internal" href="#what-you-will-need">What you will need</a> section has more information on all these
requirements.</p>
</div>
<div class="section" id="what-you-will-need">
<h1><a class="toc-backref" href="#id2">What you will need</a><a class="headerlink" href="#what-you-will-need" title="Permalink to this headline"></a></h1>
<div class="section" id="an-xmpp-jabber-server">
<h2><a class="toc-backref" href="#id3">An XMPP/Jabber server</a><a class="headerlink" href="#an-xmpp-jabber-server" title="Permalink to this headline"></a></h2>
<p><em>Converse.js</em> implements <a class="reference external" href="https://en.wikipedia.org/wiki/Xmpp">XMPP</a> as its messaging protocol, and therefore needs
to connect to an XMPP/Jabber server (Jabber is really just a synonym for XMPP).</p>
<p>You can connect to public XMPP servers like <tt class="docutils literal"><span class="pre">jabber.org</span></tt> but if you want to
have <a class="reference internal" href="#session-support">Session Support</a> you&#8217;ll have to set up your own XMPP server.</p>
<p>You can find a list of public XMPP servers/providers on <a class="reference external" href="http://xmpp.net">xmpp.net</a> and a list of
servers that you can set up yourself on <a class="reference external" href="http://xmpp.org/xmpp-software/servers/">xmpp.org</a>.</p>
</div>
<div class="section" id="connection-manager">
<h2><a class="toc-backref" href="#id4">Connection Manager</a><a class="headerlink" href="#connection-manager" title="Permalink to this headline"></a></h2>
<p>Your website and <em>Converse.js</em> use <a class="reference external" href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> as protocol to communicate with
the webserver. HTTP connections are stateless and usually shortlived.</p>
<p><a class="reference external" href="https://en.wikipedia.org/wiki/Xmpp">XMPP</a> on the other hand, is the protocol that enables instant messaging, and
its connections are stateful and usually longer.</p>
<p>To enable a web application like <em>Converse.js</em> to communicate with an XMPP
server, we need a proxy in the middle that can act as a bridge between the two
protocols.</p>
<p>This is the job of a connection manager. A connection manager can be either a
standalone application or part of an XMPP server. <a class="reference external" href="http://www.ejabberd.im">ejabberd</a> for example,
includes a connection manager (but you have to enable it).</p>
<p>The demo on the <a class="reference external" href="http://conversejs.org">Converse.js homepage</a> uses a a connection manager located at <a class="reference external" href="https://bind.opkode.im">https://bind.opkode.im</a>.
This connection manager is for testing purposes only, please don&#8217;t use it in
production.</p>
<div class="section" id="overcoming-cross-domain-request-restrictions">
<h3><a class="toc-backref" href="#id5">Overcoming cross-domain request restrictions</a><a class="headerlink" href="#overcoming-cross-domain-request-restrictions" title="Permalink to this headline"></a></h3>
<p>The domain of the <em>Converse.js</em> demo is <em>conversejs.org</em>, but the domain of the connection manager is <em>opkode.im</em>.
HTTP requests are made by <em>Converse.js</em> to the connection manager via XmlHttpRequests (XHR).
Until recently, it was not possible to make such requests to a different domain
than the one currently being served (to prevent XSS attacks).</p>
<p>Luckily there is now a standard called <a class="reference external" href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a> (Cross-origin resource sharing), which enables exactly that.
Modern browsers support CORS, but there are problems with Internet Explorer &lt;
10.</p>
<p>IE 8 and 9 partially support CORS via a proprietary implementation called
XDomainRequest. There is a <a class="reference external" href="https://gist.github.com/1095825/6b4517276f26b66b01fa97b0a78c01275fdc6ff2">Strophe.js plugin</a> which you can use to enable
support for XDomainRequest when it is present.</p>
<p>In IE &lt; 8, there is no support for CORS.</p>
<p>If you need to support these browsers, you can add a front-end proxy in
Apache/Nginx which serves the connection manager under the same domain as your
website. This will remove the need for any cross-domain XHR support.</p>
</div>
</div>
<div class="section" id="server-side-authentication">
<h2><a class="toc-backref" href="#id6">Server-side authentication</a><a class="headerlink" href="#server-side-authentication" title="Permalink to this headline"></a></h2>
<div class="section" id="pre-binding-and-single-session-support">
<span id="session-support"></span><h3><a class="toc-backref" href="#id7">Pre-binding and Single Session Support</a><a class="headerlink" href="#pre-binding-and-single-session-support" title="Permalink to this headline"></a></h3>
<p>It&#8217;s possible to enable single-site login, whereby users already
authenticated in your website will also automatically be logged in on the chat server,
but this will require custom code on your server.</p>
<p>Jack Moffitt has a great <a class="reference external" href="http://metajack.im/2008/10/03/getting-attached-to-strophe">blogpost</a> about this and even provides an <a class="reference external" href="https://github.com/metajack/strophejs/tree/master/examples/attach">example Django application</a> to demonstrate it.</p>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">If you want to enable single session support, make sure to pass <strong>prebind: true</strong>
when you call <strong>converse.initialize</strong> (see ./index.html).</p>
</div>
<p>When you authenticate to the XMPP server on your backend, you&#8217;ll receive two
tokens, RID (request ID) and SID (session ID).</p>
<p>These tokens then need to be passed back to the javascript running in your
browser, where you will need them attach to the existing session.</p>
<p>You can embed the RID and SID tokens in your HTML markup or you can do an
XMLHttpRequest call to you server and ask it to return them for you.</p>
<p>Below is one example of how this could work. An Ajax call is made to the
relative URL <strong>/prebind</strong> and it expects to receive JSON data back.</p>
<div class="highlight-python"><pre>$.getJSON('/prebind', function (data) {
var connection = new Strophe.Connection(converse.bosh_service_url);
connection.attach(data.jid, data.sid, data.rid, function (status) {
if ((status === Strophe.Status.ATTACHED) || (status === Strophe.Status.CONNECTED)) {
converse.onConnected(connection)
}
});
}
);</pre>
</div>
<p><strong>Here&#8217;s what&#8217;s happening:</strong></p>
<p>The JSON data contains the user&#8217;s JID (jabber ID), RID and SID. The URL to the
BOSH connection manager is already set as a configuration setting on the
<em>converse</em> object (see ./main.js), so we can reuse it from there.</p>
<p>A new Strophe.Connection object is instantiated and then <em>attach</em> is called with
the user&#8217;s JID, the necessary tokens and a callback function.</p>
<p>In the callback function, you call <em>converse.onConnected</em> together with the
connection object.</p>
</div>
</div>
</li>
<li><a class="reference internal" href="#minification" id="id30">Minification</a><ul>
<li><a class="reference internal" href="#minifying-javascript" id="id31">Minifying Javascript</a></li>
<li><a class="reference internal" href="#minifying-css" id="id32">Minifying CSS</a></li>
</ul>
</li>
<li><a class="reference internal" href="#translations" id="id33">Translations</a></li>
</ul>
</div>
<div class="section" id="quickstart-to-get-a-demo-up-and-running">
<h1><a class="toc-backref" href="#id8">Quickstart (to get a demo up and running)</a><a class="headerlink" href="#quickstart-to-get-a-demo-up-and-running" title="Permalink to this headline"></a></h1>
<h1><a class="toc-backref" href="#id2">Quickstart (to get a demo up and running)</a><a class="headerlink" href="#quickstart-to-get-a-demo-up-and-running" title="Permalink to this headline"></a></h1>
<p>When you download a specific release of <em>Converse.js</em> there will be two minified files inside the zip file.</p>
<ul class="simple">
<li>converse.min.js</li>
@ -248,22 +155,186 @@ logged into their XMPP session upon page reload.</p>
the minified Javascript file. Read <a class="reference internal" href="#configuration">Configuration</a> and <a class="reference internal" href="#minification">Minification</a> for more info on how to do
that.</p>
</div>
<div class="section" id="introduction">
<h1><a class="toc-backref" href="#id3">Introduction</a><a class="headerlink" href="#introduction" title="Permalink to this headline"></a></h1>
<p>Even though you can connect to public XMPP servers on the <a class="reference external" href="http://conversejs.org">conversejs.org</a>
website, <em>Converse.js</em> is not really meant to be a &#8220;Software-as-a-service&#8221; (SaaS)
webchat.</p>
<p>Instead, its goal is to provide the means for website owners to add a tightly
integrated instant messaging service to their own sites.</p>
<p>As a website owner, you are expected to host <em>Converse.js</em> yourself, and to do some legwork to
properly configure and integrate it into your site.</p>
<p>The benefit in doing this, is that your users have a much more streamlined and integrated
webchat experience and that you have control over the data. The latter being a
requirement for many sites dealing with sensitive information.</p>
<p>You&#8217;ll need to set up your own XMPP server and in order to have
<a class="reference internal" href="#session-support">Session Support</a> (i.e. single-signon functionality whereby users are authenticated once and stay
logged in to XMPP upon page reload) you will also have to add some server-side
code.</p>
<p>The <a class="reference internal" href="#what-you-will-need">What you will need</a> section has more information on all these
requirements.</p>
</div>
<div class="section" id="what-you-will-need">
<h1><a class="toc-backref" href="#id4">What you will need</a><a class="headerlink" href="#what-you-will-need" title="Permalink to this headline"></a></h1>
<div class="section" id="an-xmpp-jabber-server">
<h2><a class="toc-backref" href="#id5">An XMPP/Jabber server</a><a class="headerlink" href="#an-xmpp-jabber-server" title="Permalink to this headline"></a></h2>
<p><em>Converse.js</em> implements <a class="reference external" href="https://en.wikipedia.org/wiki/Xmpp">XMPP</a> as its messaging protocol, and therefore needs
to connect to an XMPP/Jabber server (Jabber is really just a synonym for XMPP).</p>
<p>You can connect to public XMPP servers like <tt class="docutils literal"><span class="pre">jabber.org</span></tt> but if you want to
have <a class="reference internal" href="#session-support">Session Support</a> you&#8217;ll have to set up your own XMPP server.</p>
<p>You can find a list of public XMPP servers/providers on <a class="reference external" href="http://xmpp.net">xmpp.net</a> and a list of
servers that you can set up yourself on <a class="reference external" href="http://xmpp.org/xmpp-software/servers/">xmpp.org</a>.</p>
</div>
<div class="section" id="connection-manager">
<h2><a class="toc-backref" href="#id6">Connection Manager</a><a class="headerlink" href="#connection-manager" title="Permalink to this headline"></a></h2>
<p>Your website and <em>Converse.js</em> use <a class="reference external" href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> as protocol to communicate with
the webserver. HTTP connections are stateless and usually shortlived.</p>
<p><a class="reference external" href="https://en.wikipedia.org/wiki/Xmpp">XMPP</a> on the other hand, is the protocol that enables instant messaging, and
its connections are stateful and usually longer.</p>
<p>To enable a web application like <em>Converse.js</em> to communicate with an XMPP
server, we need a proxy in the middle that can act as a bridge between the two
protocols.</p>
<p>This is the job of a connection manager. A connection manager can be either a
standalone application or part of an XMPP server. <a class="reference external" href="http://www.ejabberd.im">ejabberd</a> for example,
includes a connection manager (but you have to enable it).</p>
<p>The demo on the <a class="reference external" href="http://conversejs.org">Converse.js homepage</a> uses a a connection manager located at <a class="reference external" href="https://bind.opkode.im">https://bind.opkode.im</a>.
This connection manager is for testing purposes only, please don&#8217;t use it in
production.</p>
<div class="section" id="overcoming-cross-domain-request-restrictions">
<h3><a class="toc-backref" href="#id7">Overcoming cross-domain request restrictions</a><a class="headerlink" href="#overcoming-cross-domain-request-restrictions" title="Permalink to this headline"></a></h3>
<p>The domain of the <em>Converse.js</em> demo is <em>conversejs.org</em>, but the domain of the connection manager is <em>opkode.im</em>.
HTTP requests are made by <em>Converse.js</em> to the connection manager via XmlHttpRequests (XHR).
Until recently, it was not possible to make such requests to a different domain
than the one currently being served (to prevent XSS attacks).</p>
<p>Luckily there is now a standard called <a class="reference external" href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a> (Cross-origin resource sharing), which enables exactly that.
Modern browsers support CORS, but there are problems with Internet Explorer &lt;
10.</p>
<p>IE 8 and 9 partially support CORS via a proprietary implementation called
XDomainRequest. There is a <a class="reference external" href="https://gist.github.com/1095825/6b4517276f26b66b01fa97b0a78c01275fdc6ff2">Strophe.js plugin</a> which you can use to enable
support for XDomainRequest when it is present.</p>
<p>In IE &lt; 8, there is no support for CORS.</p>
<p>If you need to support these browsers, you can add a front-end proxy in
Apache/Nginx which serves the connection manager under the same domain as your
website. This will remove the need for any cross-domain XHR support.</p>
</div>
</div>
<div class="section" id="server-side-authentication">
<h2><a class="toc-backref" href="#id8">Server-side authentication</a><a class="headerlink" href="#server-side-authentication" title="Permalink to this headline"></a></h2>
<div class="section" id="pre-binding-and-single-session-support">
<span id="session-support"></span><h3><a class="toc-backref" href="#id9">Pre-binding and Single Session Support</a><a class="headerlink" href="#pre-binding-and-single-session-support" title="Permalink to this headline"></a></h3>
<p>It&#8217;s possible to enable single-site login, whereby users already
authenticated in your website will also automatically be logged in on the chat server,
but this will require custom code on your server.</p>
<p>Jack Moffitt has a great <a class="reference external" href="http://metajack.im/2008/10/03/getting-attached-to-strophe">blogpost</a> about this and even provides an <a class="reference external" href="https://github.com/metajack/strophejs/tree/master/examples/attach">example Django application</a> to demonstrate it.</p>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">If you want to enable single session support, make sure to pass <strong>prebind: true</strong>
when you call <strong>converse.initialize</strong> (see ./index.html).</p>
</div>
<p>When you authenticate to the XMPP server on your backend, you&#8217;ll receive two
tokens, RID (request ID) and SID (session ID).</p>
<p>These tokens then need to be passed back to the javascript running in your
browser, where you will need them attach to the existing session.</p>
<p>You can embed the RID and SID tokens in your HTML markup or you can do an
XMLHttpRequest call to you server and ask it to return them for you.</p>
<p>Below is one example of how this could work. An Ajax call is made to the
relative URL <strong>/prebind</strong> and it expects to receive JSON data back.</p>
<div class="highlight-python"><pre>$.getJSON('/prebind', function (data) {
var connection = new Strophe.Connection(converse.bosh_service_url);
connection.attach(data.jid, data.sid, data.rid, function (status) {
if ((status === Strophe.Status.ATTACHED) || (status === Strophe.Status.CONNECTED)) {
converse.onConnected(connection)
}
});
}
);</pre>
</div>
<p><strong>Here&#8217;s what&#8217;s happening:</strong></p>
<p>The JSON data contains the user&#8217;s JID (jabber ID), RID and SID. The URL to the
BOSH connection manager is already set as a configuration setting on the
<em>converse</em> object (see ./main.js), so we can reuse it from there.</p>
<p>A new Strophe.Connection object is instantiated and then <em>attach</em> is called with
the user&#8217;s JID, the necessary tokens and a callback function.</p>
<p>In the callback function, you call <em>converse.onConnected</em> together with the
connection object.</p>
</div>
</div>
</div>
<div class="section" id="development">
<h1><a class="toc-backref" href="#id9">Development</a><a class="headerlink" href="#development" title="Permalink to this headline"></a></h1>
<p>Converse.js uses <a class="reference external" href="http://requirejs.org">require.js</a> to track and load dependencies.</p>
<h1><a class="toc-backref" href="#id10">Development</a><a class="headerlink" href="#development" title="Permalink to this headline"></a></h1>
<div class="section" id="install-node-js-and-development-dependencies">
<h2><a class="toc-backref" href="#id11">Install Node.js and development dependencies</a><a class="headerlink" href="#install-node-js-and-development-dependencies" title="Permalink to this headline"></a></h2>
<p>We use development tools (<a class="reference external" href="http://gruntjs.com">Grunt</a> and <a class="reference external" href="http://bower.io">Bower</a>)
which depend on Node.js and npm (the Node package manager).</p>
<p>If you don&#8217;t have Node.js installed, you can download and install the latest
version <a class="reference external" href="https://nodejs.org/download">here</a>.</p>
<p>Once you have Node.js installed, run the following command in the Converse.js
directory:</p>
<div class="highlight-python"><pre>npm install</pre>
</div>
</div>
<div class="section" id="install-3rd-party-dependencies">
<h2><a class="toc-backref" href="#id12">Install 3rd party dependencies</a><a class="headerlink" href="#install-3rd-party-dependencies" title="Permalink to this headline"></a></h2>
<p>Now that we have Grunt and Bower, you can install and configure Converse&#8217;s
3rd party dependencies with the following command:</p>
<div class="highlight-python"><pre>grunt fetch</pre>
</div>
</div>
<div class="section" id="with-amd-and-require-js-recommended">
<h2><a class="toc-backref" href="#id13">With AMD and require.js (recommended)</a><a class="headerlink" href="#with-amd-and-require-js-recommended" title="Permalink to this headline"></a></h2>
<p>Converse.js uses <a class="reference external" href="http://requirejs.org">require.js</a> to asynchronously load dependencies.</p>
<p>If you want to develop or customize converse.js, you&#8217;ll want to load the
non-minified javascript files.</p>
<p>Add the following two lines to the <em>&lt;head&gt;</em> section of your webpage.</p>
<p>Add the following two lines to the <em>&lt;head&gt;</em> section of your webpage:</p>
<div class="highlight-python"><pre>&lt;link rel="stylesheet" type="text/css" media="screen" href="converse.css"&gt;
&lt;script data-main="main" src="Libraries/require-jquery.js"&gt;&lt;/script&gt;</pre>
&lt;script data-main="main" src="components/requirejs/require.js"&gt;&lt;/script&gt;</pre>
</div>
<p>require.js will then let the main.js file be parsed (because of the <em>data-main</em>
attribute on the <em>script</em> tag), which will in turn cause converse.js to be
parsed.</p>
</div>
<div class="section" id="without-amd-and-require-js">
<h2><a class="toc-backref" href="#id14">Without AMD and require.js</a><a class="headerlink" href="#without-amd-and-require-js" title="Permalink to this headline"></a></h2>
<p>Converse.js can also be used without require.js. If you for some reason prefer
to use it this way, please refer to <em>non_amd.html</em> for an example of how and in
what order all the Javascript files that converse.js depends on need to be
loaded.</p>
to use it this way, please refer to
<a class="reference external" href="https://github.com/jcbrand/converse.js/blob/master/non_amd.html">non_amd.html</a>
for an example of how and in what order all the Javascript files that converse.js
depends on need to be loaded.</p>
</div>
<div class="section" id="before-submitting-a-pull-request">
<h2><a class="toc-backref" href="#id15">Before submitting a pull request</a><a class="headerlink" href="#before-submitting-a-pull-request" title="Permalink to this headline"></a></h2>
<div class="section" id="add-tests-for-your-bugfix-or-feature">
<h3><a class="toc-backref" href="#id16">Add tests for your bugfix or feature</a><a class="headerlink" href="#add-tests-for-your-bugfix-or-feature" title="Permalink to this headline"></a></h3>
<p>Add a test for any bug fixed or feature added. We use Jasmine
for testing.</p>
<p>Take a look at <tt class="docutils literal"><span class="pre">tests.html</span></tt> and <tt class="docutils literal"><span class="pre">spec/MainSpec.js</span></tt> to see how
the tests are implemented.</p>
<p>If you are unsure how to write tests, please
<a class="reference external" href="http://conversejs.org">contact me</a> and I&#8217;ll be happy to help.</p>
</div>
<div class="section" id="check-that-the-tests-pass">
<h3><a class="toc-backref" href="#id17">Check that the tests pass</a><a class="headerlink" href="#check-that-the-tests-pass" title="Permalink to this headline"></a></h3>
<p>Check that the Jasmine tests complete sucessfully. Open
<a class="reference external" href="https://github.com/jcbrand/converse.js/blob/master/tests.html">tests.html</a>
in your browser, and the tests will run automatically.</p>
<p>On the command line you can run:</p>
<div class="highlight-python"><pre>grunt test</pre>
</div>
</div>
<div class="section" id="check-your-code-for-errors-or-bad-habits-by-running-jshint">
<h3><a class="toc-backref" href="#id18">Check your code for errors or bad habits by running JSHint</a><a class="headerlink" href="#check-your-code-for-errors-or-bad-habits-by-running-jshint" title="Permalink to this headline"></a></h3>
<p><a class="reference external" href="http://jshint.com">JSHint</a> will do a static analysis of your code and hightlight potential errors
and/or bad habits.</p>
<div class="highlight-python"><pre>grunt jshint</pre>
</div>
<p>You can run both the tests and jshint in one go by calling:</p>
<div class="highlight-python"><pre>grunt check</pre>
</div>
</div>
</div>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id10">Configuration</a><a class="headerlink" href="#configuration" title="Permalink to this headline"></a></h1>
<h1><a class="toc-backref" href="#id19">Configuration</a><a class="headerlink" href="#configuration" title="Permalink to this headline"></a></h1>
<p>The included minified JS and CSS files can be used for demoing or testing, but
you&#8217;ll want to configure <em>Converse.js</em> to suit your needs before you deploy it
on your website.</p>
@ -277,14 +348,14 @@ all the available configuration settings.</p>
JS file so that it will include the new settings. Please refer to the
<a class="reference internal" href="#minification">Minification</a> section for more info on how to do this.</p>
<div class="section" id="configuration-variables">
<h2><a class="toc-backref" href="#id11">Configuration variables</a><a class="headerlink" href="#configuration-variables" title="Permalink to this headline"></a></h2>
<h2><a class="toc-backref" href="#id20">Configuration variables</a><a class="headerlink" href="#configuration-variables" title="Permalink to this headline"></a></h2>
<div class="section" id="animate">
<h3><a class="toc-backref" href="#id12">animate</a><a class="headerlink" href="#animate" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id21">animate</a><a class="headerlink" href="#animate" title="Permalink to this headline"></a></h3>
<p>Default = True</p>
<p>Show animations, for example when opening and closing chat boxes.</p>
</div>
<div class="section" id="auto-list-rooms">
<h3><a class="toc-backref" href="#id13">auto_list_rooms</a><a class="headerlink" href="#auto-list-rooms" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id22">auto_list_rooms</a><a class="headerlink" href="#auto-list-rooms" title="Permalink to this headline"></a></h3>
<p>Default = False</p>
<p>If true, and the XMPP server on which the current user is logged in supports
multi-user chat, then a list of rooms on that server will be fetched.</p>
@ -294,30 +365,30 @@ features, number of occupants etc.), so on servers with many rooms this
option will create lots of extra connection traffic.</p>
</div>
<div class="section" id="auto-subscribe">
<h3><a class="toc-backref" href="#id14">auto_subscribe</a><a class="headerlink" href="#auto-subscribe" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id23">auto_subscribe</a><a class="headerlink" href="#auto-subscribe" title="Permalink to this headline"></a></h3>
<p>Default = False</p>
<p>If true, the user will automatically subscribe back to any contact requests.</p>
</div>
<div class="section" id="bosh-service-url">
<h3><a class="toc-backref" href="#id15">bosh_service_url</a><a class="headerlink" href="#bosh-service-url" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id24">bosh_service_url</a><a class="headerlink" href="#bosh-service-url" title="Permalink to this headline"></a></h3>
<p>Connections to an XMPP server depend on a BOSH connection manager which acts as
a middle man between HTTP and XMPP.</p>
<p>See <a class="reference external" href="http://metajack.im/2008/09/08/which-bosh-server-do-you-need/l">here</a> for more information.</p>
<p>See <a class="reference external" href="http://metajack.im/2008/09/08/which-bosh-server-do-you-need">here</a> for more information.</p>
</div>
<div class="section" id="fullname">
<h3><a class="toc-backref" href="#id16">fullname</a><a class="headerlink" href="#fullname" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id25">fullname</a><a class="headerlink" href="#fullname" title="Permalink to this headline"></a></h3>
<p>If you are using prebinding, you need to specify the fullname of the currently
logged in user.</p>
</div>
<div class="section" id="hide-muc-server">
<h3><a class="toc-backref" href="#id17">hide_muc_server</a><a class="headerlink" href="#hide-muc-server" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id26">hide_muc_server</a><a class="headerlink" href="#hide-muc-server" title="Permalink to this headline"></a></h3>
<p>Default = False</p>
<p>Hide the <tt class="docutils literal"><span class="pre">server</span></tt> input field of the form inside the <tt class="docutils literal"><span class="pre">Room</span></tt> panel of the
controlbox. Useful if you want to restrict users to a specific XMPP server of
your choosing.</p>
</div>
<div class="section" id="prebind">
<h3><a class="toc-backref" href="#id18">prebind</a><a class="headerlink" href="#prebind" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id27">prebind</a><a class="headerlink" href="#prebind" title="Permalink to this headline"></a></h3>
<p>Default = False</p>
<p>Use this option when you want to attach to an existing XMPP connection that was
already authenticated (usually on the backend before page load).</p>
@ -338,7 +409,7 @@ have to write a Javascript snippet to attach to the set up connection:</p>
RID (Request ID), which you use when you attach to the connection.</p>
</div>
<div class="section" id="show-controlbox-by-default">
<h3><a class="toc-backref" href="#id19">show_controlbox_by_default</a><a class="headerlink" href="#show-controlbox-by-default" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id28">show_controlbox_by_default</a><a class="headerlink" href="#show-controlbox-by-default" title="Permalink to this headline"></a></h3>
<p>Default = False</p>
<p>The &#8220;controlbox&#8221; refers to the special chatbox containing your contacts roster,
status widget, chatrooms and other controls.</p>
@ -348,7 +419,7 @@ the page with class <em>toggle-online-users</em>.</p>
page load.</p>
</div>
<div class="section" id="xhr-user-search">
<h3><a class="toc-backref" href="#id20">xhr_user_search</a><a class="headerlink" href="#xhr-user-search" title="Permalink to this headline"></a></h3>
<h3><a class="toc-backref" href="#id29">xhr_user_search</a><a class="headerlink" href="#xhr-user-search" title="Permalink to this headline"></a></h3>
<p>Default = False</p>
<p>There are two ways to add users.</p>
<ul class="simple">
@ -361,9 +432,9 @@ be used.</p>
</div>
</div>
<div class="section" id="minification">
<h1><a class="toc-backref" href="#id21">Minification</a><a class="headerlink" href="#minification" title="Permalink to this headline"></a></h1>
<h1><a class="toc-backref" href="#id30">Minification</a><a class="headerlink" href="#minification" title="Permalink to this headline"></a></h1>
<div class="section" id="minifying-javascript">
<h2><a class="toc-backref" href="#id22">Minifying Javascript</a><a class="headerlink" href="#minifying-javascript" title="Permalink to this headline"></a></h2>
<h2><a class="toc-backref" href="#id31">Minifying Javascript</a><a class="headerlink" href="#minifying-javascript" title="Permalink to this headline"></a></h2>
<p>We use <a class="reference external" href="http://requirejs.org">require.js</a> to keep track of <em>Converse.js</em> and its dependencies and to
to bundle them together in a single minified file fit for deployment to a
production site.</p>
@ -379,14 +450,14 @@ manager, NPM.</p>
<p>You can <a class="reference external" href="http://requirejs.org/docs/optimization.html">read more about require.js&#8217;s optimizer here</a>.</p>
</div>
<div class="section" id="minifying-css">
<h2><a class="toc-backref" href="#id23">Minifying CSS</a><a class="headerlink" href="#minifying-css" title="Permalink to this headline"></a></h2>
<h2><a class="toc-backref" href="#id32">Minifying CSS</a><a class="headerlink" href="#minifying-css" title="Permalink to this headline"></a></h2>
<p>CSS can be minimized with Yahoo&#8217;s yuicompressor tool:</p>
<div class="highlight-python"><pre>yui-compressor --type=css converse.css -o converse.min.css</pre>
</div>
</div>
</div>
<div class="section" id="translations">
<h1><a class="toc-backref" href="#id24">Translations</a><a class="headerlink" href="#translations" title="Permalink to this headline"></a></h1>
<h1><a class="toc-backref" href="#id33">Translations</a><a class="headerlink" href="#translations" title="Permalink to this headline"></a></h1>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">Translations take up a lot of space and will bloat your minified file.
@ -472,7 +543,7 @@ those hoops you had to jump through.</p>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li><a href="#">Converse.js 0.3 documentation</a> &raquo;</li>
<li><a href="#">Converse.js 0.5 documentation</a> &raquo;</li>
</ul>
</div>
</div>

View File

@ -1,6 +1,6 @@
# Sphinx inventory version 2
# Project: Converse.js
# Version: 0.3
# Version: 0.5
# The remainder of this file is compressed using zlib.
xÚmÎÁ
à à{Ÿ"°³ƒ]÷;

View File

@ -7,7 +7,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Search &mdash; Converse.js 0.3 documentation</title>
<title>Search &mdash; Converse.js 0.5 documentation</title>
<link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
@ -15,7 +15,7 @@
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '',
VERSION: '0.3',
VERSION: '0.5',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
@ -25,7 +25,7 @@
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/searchtools.js"></script>
<link rel="top" title="Converse.js 0.3 documentation" href="index.html" />
<link rel="top" title="Converse.js 0.5 documentation" href="index.html" />
<script type="text/javascript">
jQuery(function() { Search.loadIndex("searchindex.js"); });
</script>
@ -55,7 +55,7 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li><a href="index.html">Converse.js 0.3 documentation</a> &raquo;</li>
<li><a href="index.html">Converse.js 0.5 documentation</a> &raquo;</li>
</ul>
</div>
<section id="main_content" class="inner">
@ -100,7 +100,7 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li><a href="index.html">Converse.js 0.3 documentation</a> &raquo;</li>
<li><a href="index.html">Converse.js 0.5 documentation</a> &raquo;</li>
</ul>
</div>
</div>

View File

@ -1 +1 @@
Search.setIndex({objects:{},terms:{all:0,code:0,partial:0,queri:0,webchat:0,follow:0,middl:0,depend:0,sensit:0,sorri:0,specif:0,those:0,under:0,string:0,fals:0,mechan:0,jack:0,veri:0,list:0,pleas:0,prevent:0,past:0,second:0,pass:0,download:0,further:0,fullnam:0,click:0,even:0,index:0,what:0,hide:0,section:0,current:0,"new":0,net:0,"public":0,widget:0,gener:0,here:0,bodi:0,valu:0,box:0,convert:0,convers:0,mysit:0,reason:0,implement:0,via:0,extra:0,solut:0,prefer:0,ask:0,href:0,org:0,auto_list_room:0,instal:0,from:0,zip:0,commun:0,doubl:0,two:0,websit:0,stylesheet:0,call:0,recommend:0,type:0,until:0,tightli:0,more:0,yahoo:0,site:0,must:0,room:0,work:0,xhr:0,can:0,lc_messag:0,purpos:0,root:0,fetch:0,control:0,quickstart:0,share:0,templat:0,tag:0,proprietari:0,explor:0,onlin:0,occup:0,end:0,goal:0,snippet:0,how:0,sid:0,instead:0,css:0,updat:0,npm:0,regener:0,product:0,resourc:0,after:0,usabl:0,befor:0,callback:0,underscor:0,data:0,demonstr:0,man:0,practic:0,bind:0,show_controlbox_by_default:0,element:0,inform:0,order:0,chatbox:0,xmpp:0,over:0,through:0,streamlin:0,write:0,jid:0,directli:0,fit:0,pend:0,hidden:0,therefor:0,might:0,them:0,anim:0,"return":0,thei:0,initi:0,front:0,now:0,introduct:0,name:0,edit:0,authent:0,token:0,ejabberd:0,each:0,side:0,mean:0,domain:0,individu:0,realli:0,legwork:0,connect:0,happen:0,extract:0,special:0,variabl:0,shown:0,space:0,jabber:0,content:0,rel:0,internet:0,plural:0,factori:0,po2json:0,proxi:0,insid:0,standard:0,standalon:0,ajax:0,put:0,succesfulli:0,afterward:0,could:0,keep:0,yui:0,first:0,origin:0,softwar:0,render:0,onc:0,hoop:0,lastnam:0,number:0,yourself:0,restrict:0,alreadi:0,done:0,owner:0,open:0,differ:0,script:0,top:0,messag:0,attach:0,attack:0,jed:0,luckili:0,option:0,tool:0,specifi:0,compressor:0,part:0,exactli:0,than:0,serv:0,jump:0,kind:0,bloat:0,provid:0,remov:0,jqueri:0,bridg:0,browser:0,pre:0,saa:0,modern:0,ani:0,packag:0,have:0,tabl:0,need:0,moffitt:0,django:0,bosh_service_url:0,prebind:0,min:0,latter:0,note:0,also:0,contact:0,build:0,which:0,singl:0,sure:0,though:0,track:0,object:0,most:0,deploi:0,homepag:0,"class":0,don:0,url:0,request:0,face:0,runtim:0,xdomainrequest:0,show:0,german:0,text:0,session:0,fine:0,find:0,onli:0,locat:0,just:0,configur:0,apach:0,should:0,folder:0,local:0,meant:0,get:0,opkod:0,cannot:0,requir:0,enabl:0,emb:0,method:0,reload:0,integr:0,contain:0,where:0,set:0,stroph:0,see:0,close:0,page:0,statu:0,state:0,reus:0,between:0,experi:0,hide_muc_serv:0,attribut:0,kei:0,screen:0,javascript:0,job:0,bosh:0,roster:0,cor:0,instant:0,shortliv:0,conversej:0,etc:0,grain:0,mani:0,login:0,com:0,load:0,instanti:0,pot:0,backend:0,creat:0,rebuild:0,json:0,much:0,besid:0,subscrib:0,msgmerg:0,great:0,minifi:0,togeth:0,i18n:0,present:0,multi:0,main:0,servic:0,plugin:0,defin:0,file:0,helper:0,demo:0,auto_subscrib:0,non:0,rid:0,develop:0,minim:0,receiv:0,media:0,make:0,minif:0,cross:0,same:0,webpag:0,html:0,chatroom:0,signon:0,http:0,webserv:0,optim:0,upon:0,hand:0,"50kb":0,user:0,xhr_user_search:0,recent:0,stateless:0,markup:0,without:0,exampl:0,command:0,wherebi:0,thi:0,choos:0,usual:0,plural_form:0,protocol:0,firstnam:0,languag:0,web:0,xmlhttprequest:0,had:0,add:0,other:0,non_amd:0,input:0,yuicompressor:0,match:0,take:0,applic:0,format:0,read:0,nginx:0,traffic:0,xss:0,like:0,success:0,server:0,benefit:0,necessari:0,either:0,manag:0,deal:0,nplural:0,some:0,back:0,librari:0,bottom:0,deploy:0,overcom:0,refer:0,run:0,host:0,panel:0,src:0,about:0,controlbox:0,unfortun:0,act:0,own:0,encod:0,automat:0,wrap:0,your:0,log:0,wai:0,transfer:0,support:0,custom:0,avail:0,includ:0,lot:0,suit:0,"var":0,"function":0,head:0,properli:0,form:0,blogpost:0,bundl:0,link:0,translat:0,synonym:0,line:0,inlin:0,"true":0,congratul:0,longer:0,info:0,made:0,locale_data:0,possibl:0,"default":0,below:0,toggl:0,otherwis:0,problem:0,expect:0,featur:0,onconnect:0,exist:0,chat:0,want:0,when:0,detail:0,gettext:0,field:0,valid:0,rememb:0,test:0,you:0,nice:0,node:0,intend:0,releas:0,stai:0,lang:0,requirej:0,getjson:0,time:0},objtypes:{},titles:["Introduction"],objnames:{},filenames:["index"]})
Search.setIndex({objects:{},terms:{all:0,code:0,partial:0,queri:0,webchat:0,follow:0,middl:0,depend:0,sensit:0,sorri:0,specif:0,those:0,under:0,spec:0,string:0,fals:0,mechan:0,jack:0,veri:0,list:0,pleas:0,prevent:0,past:0,second:0,pass:0,download:0,further:0,fullnam:0,click:0,even:0,index:0,what:0,hide:0,section:0,current:0,version:0,"new":0,net:0,"public":0,widget:0,gener:0,here:0,bodi:0,let:0,valu:0,box:0,convert:0,convers:0,mysit:0,ajax:0,implement:0,via:0,extra:0,solut:0,prefer:0,put:0,href:0,org:0,auto_list_room:0,instal:0,from:0,zip:0,commun:0,doubl:0,two:0,websit:0,stylesheet:0,call:0,recommend:0,type:0,until:0,tightli:0,more:0,yahoo:0,must:0,room:0,work:0,xhr:0,can:0,lc_messag:0,purpos:0,root:0,fetch:0,control:0,quickstart:0,share:0,templat:0,tag:0,proprietari:0,explor:0,onlin:0,occup:0,end:0,goal:0,snippet:0,how:0,sid:0,roster:0,instead:0,css:0,updat:0,npm:0,regener:0,product:0,resourc:0,after:0,usabl:0,befor:0,underscor:0,data:0,demonstr:0,man:0,practic:0,bind:0,show_controlbox_by_default:0,element:0,caus:0,callback:0,parti:0,order:0,help:0,chatbox:0,xmpp:0,over:0,becaus:0,through:0,streamlin:0,write:0,jid:0,directli:0,fit:0,fix:0,"static":0,pend:0,hidden:0,therefor:0,might:0,them:0,anim:0,"return":0,thei:0,initi:0,front:0,now:0,introduct:0,name:0,edit:0,authent:0,token:0,ejabberd:0,each:0,side:0,mean:0,domain:0,individu:0,realli:0,legwork:0,connect:0,happen:0,extract:0,special:0,variabl:0,shown:0,"3rd":0,space:0,open:0,content:0,rel:0,internet:0,plural:0,factori:0,po2json:0,proxi:0,insid:0,standard:0,standalon:0,reason:0,ask:0,succesfulli:0,afterward:0,could:0,keep:0,yui:0,turn:0,first:0,origin:0,softwar:0,render:0,onc:0,hoop:0,lastnam:0,number:0,yourself:0,restrict:0,alreadi:0,done:0,owner:0,custom:0,jabber:0,differ:0,script:0,top:0,messag:0,attach:0,attack:0,jed:0,luckili:0,option:0,tool:0,specifi:0,compressor:0,part:0,pars:0,grunt:0,than:0,serv:0,jump:0,kind:0,bloat:0,provid:0,remov:0,exampl:0,bridg:0,browser:0,pre:0,"function":0,saa:0,modern:0,ani:0,packag:0,have:0,tabl:0,need:0,moffitt:0,django:0,bosh_service_url:0,prebind:0,min:0,latter:0,note:0,also:0,contact:0,take:0,which:0,singl:0,sure:0,though:0,unsur:0,object:0,most:0,deploi:0,homepag:0,"class":0,don:0,url:0,request:0,face:0,runtim:0,bower:0,latest:0,xdomainrequest:0,show:0,german:0,text:0,session:0,fine:0,find:0,onli:0,exactli:0,locat:0,just:0,configur:0,apach:0,should:0,folder:0,local:0,meant:0,get:0,opkod:0,cannot:0,requir:0,enabl:0,emb:0,mainspec:0,method:0,reload:0,bad:0,integr:0,contain:0,where:0,set:0,habit:0,stroph:0,see:0,close:0,page:0,statu:0,state:0,reus:0,between:0,experi:0,jasmin:0,hide_muc_serv:0,attribut:0,kei:0,screen:0,javascript:0,job:0,bosh:0,both:0,cor:0,instant:0,shortliv:0,conversej:0,etc:0,grain:0,mani:0,login:0,com:0,load:0,instanti:0,pot:0,non:0,backend:0,sucessfulli:0,rebuild:0,compon:0,json:0,much:0,besid:0,subscrib:0,msgmerg:0,great:0,minifi:0,togeth:0,i18n:0,present:0,multi:0,main:0,look:0,servic:0,plugin:0,defin:0,error:0,hightlight:0,chat:0,helper:0,demo:0,auto_subscrib:0,site:0,inform:0,rid:0,develop:0,minim:0,receiv:0,media:0,make:0,minif:0,cross:0,same:0,webpag:0,html:0,chatroom:0,complet:0,signon:0,http:0,webserv:0,optim:0,upon:0,hand:0,"50kb":0,user:0,xhr_user_search:0,recent:0,stateless:0,markup:0,without:0,command:0,wherebi:0,thi:0,choos:0,usual:0,plural_form:0,protocol:0,firstnam:0,jshint:0,languag:0,web:0,xmlhttprequest:0,had:0,onconnect:0,add:0,other:0,non_amd:0,input:0,yuicompressor:0,match:0,build:0,applic:0,format:0,read:0,amd:0,nginx:0,traffic:0,xss:0,like:0,success:0,server:0,benefit:0,necessari:0,either:0,manag:0,deal:0,nplural:0,some:0,back:0,librari:0,bottom:0,deploy:0,track:0,overcom:0,refer:0,run:0,host:0,panel:0,src:0,about:0,controlbox:0,unfortun:0,act:0,own:0,encod:0,automat:0,wrap:0,your:0,log:0,wai:0,transfer:0,support:0,submit:0,happi:0,avail:0,includ:0,lot:0,suit:0,"var":0,analysi:0,head:0,properli:0,form:0,blogpost:0,bundl:0,link:0,translat:0,synonym:0,line:0,inlin:0,"true":0,bug:0,congratul:0,requirej:0,info:0,pull:0,made:0,locale_data:0,possibl:0,"default":0,bugfix:0,asynchron:0,below:0,toggl:0,otherwis:0,problem:0,expect:0,featur:0,creat:0,exist:0,file:0,check:0,want:0,when:0,detail:0,gettext:0,field:0,valid:0,rememb:0,test:0,you:0,nice:0,node:0,intend:0,releas:0,stai:0,lang:0,longer:0,directori:0,getjson:0,potenti:0,time:0},objtypes:{},titles:["Quickstart (to get a demo up and running)"],objnames:{},filenames:["index"]})

View File

@ -48,9 +48,9 @@ copyright = u'2013, JC Brand'
# built documents.
#
# The short X.Y version.
version = '0.3'
version = '0.5'
# The full version, including alpha/beta/rc tags.
release = '0.3'
release = '0.5'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -10,6 +10,60 @@
:depth: 3
:local:
=========================================
Quickstart (to get a demo up and running)
=========================================
When you download a specific release of *Converse.js* there will be two minified files inside the zip file.
* converse.min.js
* converse.min.css
You can include these two files inside the *<head>* element of your website via the *script* and *link*
tags:
::
<link rel="stylesheet" type="text/css" media="screen" href="converse.min.css">
<script src="converse.min.js"></script>
Then, at the bottom of your page, after the closing *</body>* element, put the
following inline Javascript code:
::
require(['converse'], function (converse) {
converse.initialize({
auto_list_rooms: false,
auto_subscribe: false,
bosh_service_url: 'https://bind.opkode.im', // Please use this connection manager only for testing purposes
hide_muc_server: false,
i18n: locales.en, // Refer to ./locale/locales.js to see which locales are supported
prebind: false,
show_controlbox_by_default: true,
xhr_user_search: false
});
});
The *index.html* file inside the Converse.js folder serves as a nice usable
example of this.
These minified files provide the same demo-like functionality as is available
on the `conversejs.org`_ website. Useful for testing or demoing, but not very
practical.
You'll most likely want to implement some kind of single-signon solution for
your website, where users authenticate once in your website and then stay
logged into their XMPP session upon page reload.
For more info on this, read: `Pre-binding and Single Session Support`_.
You might also want to have more fine-grained control of what gets included in
the minified Javascript file. Read `Configuration`_ and `Minification`_ for more info on how to do
that.
============
Introduction
============
@ -36,6 +90,7 @@ code.
The `What you will need`_ section has more information on all these
requirements.
==================
What you will need
==================
@ -151,85 +206,109 @@ In the callback function, you call *converse.onConnected* together with the
connection object.
=========================================
Quickstart (to get a demo up and running)
=========================================
When you download a specific release of *Converse.js* there will be two minified files inside the zip file.
* converse.min.js
* converse.min.css
You can include these two files inside the *<head>* element of your website via the *script* and *link*
tags:
::
<link rel="stylesheet" type="text/css" media="screen" href="converse.min.css">
<script src="converse.min.js"></script>
Then, at the bottom of your page, after the closing *</body>* element, put the
following inline Javascript code:
::
require(['converse'], function (converse) {
converse.initialize({
auto_list_rooms: false,
auto_subscribe: false,
bosh_service_url: 'https://bind.opkode.im', // Please use this connection manager only for testing purposes
hide_muc_server: false,
i18n: locales.en, // Refer to ./locale/locales.js to see which locales are supported
prebind: false,
show_controlbox_by_default: true,
xhr_user_search: false
});
});
The *index.html* file inside the Converse.js folder serves as a nice usable
example of this.
These minified files provide the same demo-like functionality as is available
on the `conversejs.org`_ website. Useful for testing or demoing, but not very
practical.
You'll most likely want to implement some kind of single-signon solution for
your website, where users authenticate once in your website and then stay
logged into their XMPP session upon page reload.
For more info on this, read: `Pre-binding and Single Session Support`_.
You might also want to have more fine-grained control of what gets included in
the minified Javascript file. Read `Configuration`_ and `Minification`_ for more info on how to do
that.
===========
Development
===========
With AMD and require.js (recommended)
-------------------------------------
Install Node.js and development dependencies
============================================
Converse.js uses `require.js`_ to track and load dependencies.
We use development tools (`Grunt <http://gruntjs.com>`_ and `Bower <http://bower.io>`_)
which depend on Node.js and npm (the Node package manager).
If you don't have Node.js installed, you can download and install the latest
version `here <https://nodejs.org/download>`_.
Once you have Node.js installed, run the following command in the Converse.js
directory:
::
npm install
Install 3rd party dependencies
==============================
Now that we have Grunt and Bower, you can install and configure Converse's
3rd party dependencies with the following command:
::
grunt fetch
With AMD and require.js (recommended)
=====================================
Converse.js uses `require.js`_ to asynchronously load dependencies.
If you want to develop or customize converse.js, you'll want to load the
non-minified javascript files.
Add the following two lines to the *<head>* section of your webpage.
Add the following two lines to the *<head>* section of your webpage:
::
<link rel="stylesheet" type="text/css" media="screen" href="converse.css">
<script data-main="main" src="Libraries/require-jquery.js"></script>
<script data-main="main" src="components/requirejs/require.js"></script>
require.js will then let the main.js file be parsed (because of the *data-main*
attribute on the *script* tag), which will in turn cause converse.js to be
parsed.
Without AMD and require.js
--------------------------
==========================
Converse.js can also be used without require.js. If you for some reason prefer
to use it this way, please refer to *non_amd.html* for an example of how and in
what order all the Javascript files that converse.js depends on need to be
loaded.
to use it this way, please refer to
`non_amd.html <https://github.com/jcbrand/converse.js/blob/master/non_amd.html>`_
for an example of how and in what order all the Javascript files that converse.js
depends on need to be loaded.
Before submitting a pull request
================================
Add tests for your bugfix or feature
------------------------------------
Add a test for any bug fixed or feature added. We use Jasmine
for testing.
Take a look at ``tests.html`` and ``spec/MainSpec.js`` to see how
the tests are implemented.
If you are unsure how to write tests, please
`contact me <http://conversejs.org>`_ and I'll be happy to help.
Check that the tests pass
-------------------------
Check that the Jasmine tests complete sucessfully. Open
`tests.html <https://github.com/jcbrand/converse.js/blob/master/tests.html>`_
in your browser, and the tests will run automatically.
On the command line you can run:
::
grunt test
Check your code for errors or bad habits by running JSHint
----------------------------------------------------------
`JSHint <http://jshint.com>`_ will do a static analysis of your code and hightlight potential errors
and/or bad habits.
::
grunt jshint
You can run both the tests and jshint in one go by calling:
::
grunt check
=============
@ -290,7 +369,7 @@ bosh_service_url
Connections to an XMPP server depend on a BOSH connection manager which acts as
a middle man between HTTP and XMPP.
See `here`_ for more information.
See `here <http://metajack.im/2008/09/08/which-bosh-server-do-you-need>`_ for more information.
fullname
--------
@ -505,7 +584,6 @@ those hoops you had to jump through.
.. _`conversejs.org`: http://conversejs.org
.. _`require.js`: http://requirejs.org
.. _`read more about require.js's optimizer here`: http://requirejs.org/docs/optimization.html
.. _`here`: http://metajack.im/2008/09/08/which-bosh-server-do-you-need/l
.. _`HTTP`: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
.. _`XMPP`: https://en.wikipedia.org/wiki/Xmpp
.. _`Converse.js homepage`: http://conversejs.org

View File

@ -5,8 +5,8 @@
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="description" content="Converse.js: Open Source Browser-Based Instant Messaging" />
<link rel="stylesheet" type="text/css" media="screen" href="stylesheets/stylesheet.css">
<link rel="stylesheet" type="text/css" media="screen" href="converse.min.css">
<script src="converse.min.js"></script>
<link rel="stylesheet" type="text/css" media="screen" href="converse-0.5.0.min.css">
<script src="converse-0.5.0.min.js"></script>
<!-- For development: <script data-main="main" src="Libraries/require-jquery.js"></script> -->
<title>Converse.js</title>
</head>
@ -64,7 +64,7 @@
<li>Custom status messages</li>
<li>Typing notifications</li>
<li>Third person messages (/me )</li>
<li>i18n aware</li>
<li>Translated into multiple languages (af, de, es, it, pt_BR)</li>
</ul>
<h2>Screencasts</h2>

View File

@ -1 +0,0 @@
console.log('This would be the main JS file.');

View File

@ -1,5 +1,5 @@
(function (root, factory) {
var af = new Jed({
var translations = {
"domain": "converse",
"locale_data": {
"converse": {
@ -450,16 +450,16 @@
]
}
}
});
};
if (typeof define === 'function' && define.amd) {
define("af", ['jed'], function () {
return factory(af);
return factory(new Jed(translations));
});
} else {
if (!window.locales) {
window.locales = {};
}
window.locales.af = af;
window.locales.af = factory(new Jed(translations));
}
}(this, function (af) {
return af;

View File

@ -1,5 +1,5 @@
(function (root, factory) {
var de = new Jed({
var translations = {
"domain": "converse",
"locale_data": {
"converse": {
@ -450,16 +450,16 @@
]
}
}
});
};
if (typeof define === 'function' && define.amd) {
define("de", ['jed'], function () {
return factory(de);
return factory(new Jed(translations));
});
} else {
if (!window.locales) {
window.locales = {};
}
window.locales.de = de;
window.locales.de = factory(new Jed(translations));
}
}(this, function (de) {
return de;

View File

@ -1,5 +1,5 @@
(function (root, factory) {
var en = new Jed({
var translations = {
"domain": "converse",
"locale_data": {
"converse": {
@ -10,16 +10,16 @@
}
}
}
});
};
if (typeof define === 'function' && define.amd) {
define("en", ['jed'], function () {
return factory(en);
return factory(new Jed(translations));
});
} else {
if (!window.locales) {
window.locales = {};
}
window.locales.en = en;
window.locales.en = factory(new Jed(translations));
}
}(this, function (en) {
return en;

View File

@ -1,5 +1,5 @@
(function (root, factory) {
var es = new Jed({
var translations = {
"domain": "converse",
"locale_data": {
"converse": {
@ -450,16 +450,16 @@
]
}
}
});
};
if (typeof define === 'function' && define.amd) {
define("es", ['jed'], function () {
return factory(es);
return factory(new Jed(translations));
});
} else {
if (!window.locales) {
window.locales = {};
}
window.locales.es = es;
window.locales.es = factory(new Jed(translations));
}
}(this, function (es) {
return es;

View File

@ -1,457 +0,0 @@
{
"converse": {
"": {
"Project-Id-Version": "Converse.js 0.4",
"Report-Msgid-Bugs-To": "",
"POT-Creation-Date": "2013-06-01 23:03+0200",
"PO-Revision-Date": "2013-06-02 19:45+0200",
"Last-Translator": "JC Brand <jc@opkode.com>",
"Language-Team": "Hungarian",
"Language": "hu",
"MIME-Version": "1.0",
"Content-Type": "text/plain; charset=ASCII",
"Content-Transfer-Encoding": "8bit",
"Plural-Forms": "nplurals=2; plural=(n != 1);"
},
"Show this menu": [
null,
""
],
"Write in the third person": [
null,
""
],
"Remove messages": [
null,
""
],
"Personal message": [
null,
""
],
"Contacts": [
null,
""
],
"Online": [
null,
""
],
"Busy": [
null,
""
],
"Away": [
null,
""
],
"Offline": [
null,
""
],
"Click to add new chat contacts": [
null,
""
],
"Add a contact": [
null,
""
],
"Contact username": [
null,
""
],
"Add": [
null,
""
],
"Contact name": [
null,
""
],
"Search": [
null,
""
],
"No users found": [
null,
""
],
"Click to add as a chat contact": [
null,
""
],
"Click to open this room": [
null,
""
],
"Show more information on this room": [
null,
""
],
"Description:": [
null,
""
],
"Occupants:": [
null,
""
],
"Features:": [
null,
""
],
"Requires authentication": [
null,
""
],
"Hidden": [
null,
""
],
"Requires an invitation": [
null,
""
],
"Moderated": [
null,
""
],
"Non-anonymous": [
null,
""
],
"Open room": [
null,
""
],
"Permanent room": [
null,
""
],
"Public": [
null,
""
],
"Semi-anonymous": [
null,
""
],
"Temporary room": [
null,
""
],
"Unmoderated": [
null,
""
],
"Rooms": [
null,
""
],
"Room name": [
null,
""
],
"Nickname": [
null,
""
],
"Server": [
null,
""
],
"Join": [
null,
""
],
"Show rooms": [
null,
""
],
"No rooms on %1$s": [
null,
""
],
"Rooms on %1$s": [
null,
""
],
"Set chatroom topic": [
null,
""
],
"Kick user from chatroom": [
null,
""
],
"Ban user from chatroom": [
null,
""
],
"Message": [
null,
""
],
"Save": [
null,
""
],
"Cancel": [
null,
""
],
"An error occurred while trying to save the form.": [
null,
""
],
"This chatroom requires a password": [
null,
""
],
"Password: ": [
null,
""
],
"Submit": [
null,
""
],
"This room is not anonymous": [
null,
""
],
"This room now shows unavailable members": [
null,
""
],
"This room does not show unavailable members": [
null,
""
],
"Non-privacy-related room configuration has changed": [
null,
""
],
"Room logging is now enabled": [
null,
""
],
"Room logging is now disabled": [
null,
""
],
"This room is now non-anonymous": [
null,
""
],
"This room is now semi-anonymous": [
null,
""
],
"This room is now fully-anonymous": [
null,
""
],
"A new room has been created": [
null,
""
],
"Your nickname has been changed": [
null,
""
],
"<strong>%1$s</strong> has been banned": [
null,
""
],
"<strong>%1$s</strong> has been kicked out": [
null,
""
],
"<strong>%1$s</strong> has been removed because of an affiliation change": [
null,
""
],
"<strong>%1$s</strong> has been removed for not being a member": [
null,
""
],
"You have been banned from this room": [
null,
""
],
"You have been kicked from this room": [
null,
""
],
"You have been removed from this room because of an affiliation change": [
null,
""
],
"You have been removed from this room because the room has changed to members-only and you're not a member": [
null,
""
],
"You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
null,
""
],
"You are not on the member list of this room": [
null,
""
],
"No nickname was specified": [
null,
""
],
"You are not allowed to create new rooms": [
null,
""
],
"Your nickname doesn't conform to this room's policies": [
null,
""
],
"Your nickname is already taken": [
null,
""
],
"This room does not (yet) exist": [
null,
""
],
"This room has reached it's maximum number of occupants": [
null,
""
],
"Topic set by %1$s to: %2$s": [
null,
""
],
"This user is a moderator": [
null,
""
],
"This user can send messages in this room": [
null,
""
],
"This user can NOT send messages in this room": [
null,
""
],
"Click to chat with this contact": [
null,
""
],
"Click to remove this contact": [
null,
""
],
"Contact requests": [
null,
""
],
"My contacts": [
null,
""
],
"Pending contacts": [
null,
""
],
"Custom status": [
null,
""
],
"Click to change your chat status": [
null,
""
],
"Click here to write a custom status message": [
null,
""
],
"online": [
null,
""
],
"busy": [
null,
""
],
"away for long": [
null,
""
],
"away": [
null,
""
],
"I am %1$s": [
null,
""
],
"Sign in": [
null,
""
],
"XMPP/Jabber Username:": [
null,
""
],
"Password:": [
null,
""
],
"Log In": [
null,
""
],
"BOSH Service URL:": [
null,
""
],
"Connected": [
null,
""
],
"Disconnected": [
null,
""
],
"Error": [
null,
""
],
"Connecting": [
null,
""
],
"Connection Failed": [
null,
""
],
"Authenticating": [
null,
""
],
"Authentication Failed": [
null,
""
],
"Disconnecting": [
null,
""
],
"Attached": [
null,
""
],
"Online Contacts": [
null,
""
]
}
}

View File

@ -1,481 +0,0 @@
# Hungarian translations for Converse.js package.
# Copyright (C) 2013 Jan-Carel Brand
# This file is distributed under the same license as the Converse.js package.
# JC Brand <jc@opkode.com>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: Converse.js 0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-06-01 23:03+0200\n"
"PO-Revision-Date: 2013-06-02 19:45+0200\n"
"Last-Translator: JC Brand <jc@opkode.com>\n"
"Language-Team: Hungarian\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ASCII\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: converse.js:397 converse.js:1128
msgid "Show this menu"
msgstr ""
#: converse.js:398 converse.js:1129
msgid "Write in the third person"
msgstr ""
#: converse.js:399 converse.js:1133
msgid "Remove messages"
msgstr ""
#: converse.js:539
msgid "Personal message"
msgstr ""
#: converse.js:613
msgid "Contacts"
msgstr ""
#: converse.js:618
msgid "Online"
msgstr ""
#: converse.js:619
msgid "Busy"
msgstr ""
#: converse.js:620
msgid "Away"
msgstr ""
#: converse.js:621
msgid "Offline"
msgstr ""
#: converse.js:628
msgid "Click to add new chat contacts"
msgstr ""
#: converse.js:628
msgid "Add a contact"
msgstr ""
#: converse.js:637
msgid "Contact username"
msgstr ""
#: converse.js:638
msgid "Add"
msgstr ""
#: converse.js:646
msgid "Contact name"
msgstr ""
#: converse.js:647
msgid "Search"
msgstr ""
#: converse.js:682
msgid "No users found"
msgstr ""
#: converse.js:689
msgid "Click to add as a chat contact"
msgstr ""
#: converse.js:753
msgid "Click to open this room"
msgstr ""
#: converse.js:755
msgid "Show more information on this room"
msgstr ""
#: converse.js:760
msgid "Description:"
msgstr ""
#: converse.js:761
msgid "Occupants:"
msgstr ""
#: converse.js:762
msgid "Features:"
msgstr ""
#: converse.js:764
msgid "Requires authentication"
msgstr ""
#: converse.js:767
msgid "Hidden"
msgstr ""
#: converse.js:770
msgid "Requires an invitation"
msgstr ""
#: converse.js:773
msgid "Moderated"
msgstr ""
#: converse.js:776
msgid "Non-anonymous"
msgstr ""
#: converse.js:779
msgid "Open room"
msgstr ""
#: converse.js:782
msgid "Permanent room"
msgstr ""
#: converse.js:785
msgid "Public"
msgstr ""
#: converse.js:788
msgid "Semi-anonymous"
msgstr ""
#: converse.js:791
msgid "Temporary room"
msgstr ""
#: converse.js:794
msgid "Unmoderated"
msgstr ""
#: converse.js:800
msgid "Rooms"
msgstr ""
#: converse.js:804
msgid "Room name"
msgstr ""
#: converse.js:805
msgid "Nickname"
msgstr ""
#: converse.js:806
msgid "Server"
msgstr ""
#: converse.js:807
msgid "Join"
msgstr ""
#: converse.js:808
msgid "Show rooms"
msgstr ""
#. For translators: %1$s is a variable and will be replaced with the XMPP server name
#: converse.js:841
msgid "No rooms on %1$s"
msgstr ""
#. For translators: %1$s is a variable and will be
#. replaced with the XMPP server name
#: converse.js:856
msgid "Rooms on %1$s"
msgstr ""
#: converse.js:1130
msgid "Set chatroom topic"
msgstr ""
#: converse.js:1131
msgid "Kick user from chatroom"
msgstr ""
#: converse.js:1132
msgid "Ban user from chatroom"
msgstr ""
#: converse.js:1159
msgid "Message"
msgstr ""
#: converse.js:1273 converse.js:2318
msgid "Save"
msgstr ""
#: converse.js:1274
msgid "Cancel"
msgstr ""
#: converse.js:1321
msgid "An error occurred while trying to save the form."
msgstr ""
#: converse.js:1367
msgid "This chatroom requires a password"
msgstr ""
#: converse.js:1368
msgid "Password: "
msgstr ""
#: converse.js:1369
msgid "Submit"
msgstr ""
#: converse.js:1383
msgid "This room is not anonymous"
msgstr ""
#: converse.js:1384
msgid "This room now shows unavailable members"
msgstr ""
#: converse.js:1385
msgid "This room does not show unavailable members"
msgstr ""
#: converse.js:1386
msgid "Non-privacy-related room configuration has changed"
msgstr ""
#: converse.js:1387
msgid "Room logging is now enabled"
msgstr ""
#: converse.js:1388
msgid "Room logging is now disabled"
msgstr ""
#: converse.js:1389
msgid "This room is now non-anonymous"
msgstr ""
#: converse.js:1390
msgid "This room is now semi-anonymous"
msgstr ""
#: converse.js:1391
msgid "This room is now fully-anonymous"
msgstr ""
#: converse.js:1392
msgid "A new room has been created"
msgstr ""
#: converse.js:1393
msgid "Your nickname has been changed"
msgstr ""
#. For translations: %1$s will be replaced with the user's nickname
#. Don't translate "strong"
#. Example: <strong>jcbrand</strong> has been banned
#: converse.js:1400
msgid "<strong>%1$s</strong> has been banned"
msgstr ""
#. For translations: %1$s will be replaced with the user's nickname
#. Don't translate "strong"
#. Example: <strong>jcbrand</strong> has been kicked out
#: converse.js:1404
msgid "<strong>%1$s</strong> has been kicked out"
msgstr ""
#. For translations: %1$s will be replaced with the user's nickname
#. Don't translate "strong"
#. Example: <strong>jcbrand</strong> has been removed because of an affiliasion change
#: converse.js:1408
msgid "<strong>%1$s</strong> has been removed because of an affiliation change"
msgstr ""
#. For translations: %1$s will be replaced with the user's nickname
#. Don't translate "strong"
#. Example: <strong>jcbrand</strong> has been removed for not being a member
#: converse.js:1412
msgid "<strong>%1$s</strong> has been removed for not being a member"
msgstr ""
#: converse.js:1416 converse.js:1478
msgid "You have been banned from this room"
msgstr ""
#: converse.js:1417
msgid "You have been kicked from this room"
msgstr ""
#: converse.js:1418
msgid "You have been removed from this room because of an affiliation change"
msgstr ""
#: converse.js:1419
msgid ""
"You have been removed from this room because the room has changed to members-"
"only and you're not a member"
msgstr ""
#: converse.js:1420
msgid ""
"You have been removed from this room because the MUC (Multi-user chat) "
"service is being shut down."
msgstr ""
#: converse.js:1476
msgid "You are not on the member list of this room"
msgstr ""
#: converse.js:1482
msgid "No nickname was specified"
msgstr ""
#: converse.js:1486
msgid "You are not allowed to create new rooms"
msgstr ""
#: converse.js:1488
msgid "Your nickname doesn't conform to this room's policies"
msgstr ""
#: converse.js:1490
msgid "Your nickname is already taken"
msgstr ""
#: converse.js:1492
msgid "This room does not (yet) exist"
msgstr ""
#: converse.js:1494
msgid "This room has reached it's maximum number of occupants"
msgstr ""
#. For translators: the %1$s and %2$s parts will get replaced by the user and topic text respectively
#. Example: Topic set by JC Brand to: Hello World!
#: converse.js:1571
msgid "Topic set by %1$s to: %2$s"
msgstr ""
#: converse.js:1587
msgid "This user is a moderator"
msgstr ""
#: converse.js:1590
msgid "This user can send messages in this room"
msgstr ""
#: converse.js:1593
msgid "This user can NOT send messages in this room"
msgstr ""
#: converse.js:1796
msgid "Click to chat with this contact"
msgstr ""
#: converse.js:1797 converse.js:1801
msgid "Click to remove this contact"
msgstr ""
#: converse.js:2163
msgid "Contact requests"
msgstr ""
#: converse.js:2164
msgid "My contacts"
msgstr ""
#: converse.js:2165
msgid "Pending contacts"
msgstr ""
#: converse.js:2317
msgid "Custom status"
msgstr ""
#: converse.js:2323
msgid "Click to change your chat status"
msgstr ""
#: converse.js:2326
msgid "Click here to write a custom status message"
msgstr ""
#: converse.js:2355 converse.js:2363
msgid "online"
msgstr ""
#: converse.js:2357
msgid "busy"
msgstr ""
#: converse.js:2359
msgid "away for long"
msgstr ""
#: converse.js:2361
msgid "away"
msgstr ""
#. For translators: the %1$s part gets replaced with the status
#. Example, I am online
#: converse.js:2375 converse.js:2409
msgid "I am %1$s"
msgstr ""
#: converse.js:2480
msgid "Sign in"
msgstr ""
#: converse.js:2483
msgid "XMPP/Jabber Username:"
msgstr ""
#: converse.js:2485
msgid "Password:"
msgstr ""
#: converse.js:2487
msgid "Log In"
msgstr ""
#: converse.js:2491
msgid "BOSH Service URL:"
msgstr ""
#: converse.js:2503
msgid "Connected"
msgstr ""
#: converse.js:2507
msgid "Disconnected"
msgstr ""
#: converse.js:2511
msgid "Error"
msgstr ""
#: converse.js:2513
msgid "Connecting"
msgstr ""
#: converse.js:2516
msgid "Connection Failed"
msgstr ""
#: converse.js:2518
msgid "Authenticating"
msgstr ""
#: converse.js:2521
msgid "Authentication Failed"
msgstr ""
#: converse.js:2523
msgid "Disconnecting"
msgstr ""
#: converse.js:2525
msgid "Attached"
msgstr ""
#: converse.js:2656
msgid "Online Contacts"
msgstr ""

View File

@ -1,459 +0,0 @@
(function (root, factory) {
define("hu", ['jed'], function () {
var hu = new Jed({
"domain": "converse",
"locale_data": {
"converse": {
"": {
"Content-Type": "text/plain; charset=ASCII",
"Content-Transfer-Encoding": "8bit",
"Plural-Forms": "nplurals=2; plural=(n != 1);"
},
"Show this menu": [
null,
""
],
"Write in the third person": [
null,
""
],
"Remove messages": [
null,
""
],
"Personal message": [
null,
""
],
"Contacts": [
null,
""
],
"Online": [
null,
""
],
"Busy": [
null,
""
],
"Away": [
null,
""
],
"Offline": [
null,
""
],
"Click to add new chat contacts": [
null,
""
],
"Add a contact": [
null,
""
],
"Contact username": [
null,
""
],
"Add": [
null,
""
],
"Contact name": [
null,
""
],
"Search": [
null,
""
],
"No users found": [
null,
""
],
"Click to add as a chat contact": [
null,
""
],
"Click to open this room": [
null,
""
],
"Show more information on this room": [
null,
""
],
"Description:": [
null,
""
],
"Occupants:": [
null,
""
],
"Features:": [
null,
""
],
"Requires authentication": [
null,
""
],
"Hidden": [
null,
""
],
"Requires an invitation": [
null,
""
],
"Moderated": [
null,
""
],
"Non-anonymous": [
null,
""
],
"Open room": [
null,
""
],
"Permanent room": [
null,
""
],
"Public": [
null,
""
],
"Semi-anonymous": [
null,
""
],
"Temporary room": [
null,
""
],
"Unmoderated": [
null,
""
],
"Rooms": [
null,
""
],
"Room name": [
null,
""
],
"Nickname": [
null,
""
],
"Server": [
null,
""
],
"Join": [
null,
""
],
"Show rooms": [
null,
""
],
"No rooms on %1$s": [
null,
""
],
"Rooms on %1$s": [
null,
""
],
"Set chatroom topic": [
null,
""
],
"Kick user from chatroom": [
null,
""
],
"Ban user from chatroom": [
null,
""
],
"Message": [
null,
""
],
"Save": [
null,
""
],
"Cancel": [
null,
""
],
"An error occurred while trying to save the form.": [
null,
""
],
"This chatroom requires a password": [
null,
""
],
"Password: ": [
null,
""
],
"Submit": [
null,
""
],
"This room is not anonymous": [
null,
""
],
"This room now shows unavailable members": [
null,
""
],
"This room does not show unavailable members": [
null,
""
],
"Non-privacy-related room configuration has changed": [
null,
""
],
"Room logging is now enabled": [
null,
""
],
"Room logging is now disabled": [
null,
""
],
"This room is now non-anonymous": [
null,
""
],
"This room is now semi-anonymous": [
null,
""
],
"This room is now fully-anonymous": [
null,
""
],
"A new room has been created": [
null,
""
],
"Your nickname has been changed": [
null,
""
],
"<strong>%1$s</strong> has been banned": [
null,
""
],
"<strong>%1$s</strong> has been kicked out": [
null,
""
],
"<strong>%1$s</strong> has been removed because of an affiliation change": [
null,
""
],
"<strong>%1$s</strong> has been removed for not being a member": [
null,
""
],
"You have been banned from this room": [
null,
""
],
"You have been kicked from this room": [
null,
""
],
"You have been removed from this room because of an affiliation change": [
null,
""
],
"You have been removed from this room because the room has changed to members-only and you're not a member": [
null,
""
],
"You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
null,
""
],
"You are not on the member list of this room": [
null,
""
],
"No nickname was specified": [
null,
""
],
"You are not allowed to create new rooms": [
null,
""
],
"Your nickname doesn't conform to this room's policies": [
null,
""
],
"Your nickname is already taken": [
null,
""
],
"This room does not (yet) exist": [
null,
""
],
"This room has reached it's maximum number of occupants": [
null,
""
],
"Topic set by %1$s to: %2$s": [
null,
""
],
"This user is a moderator": [
null,
""
],
"This user can send messages in this room": [
null,
""
],
"This user can NOT send messages in this room": [
null,
""
],
"Click to chat with this contact": [
null,
""
],
"Click to remove this contact": [
null,
""
],
"Contact requests": [
null,
""
],
"My contacts": [
null,
""
],
"Pending contacts": [
null,
""
],
"Custom status": [
null,
""
],
"Click to change your chat status": [
null,
""
],
"Click here to write a custom status message": [
null,
""
],
"online": [
null,
""
],
"busy": [
null,
""
],
"away for long": [
null,
""
],
"away": [
null,
""
],
"I am %1$s": [
null,
""
],
"Sign in": [
null,
""
],
"XMPP/Jabber Username:": [
null,
""
],
"Password:": [
null,
""
],
"Log In": [
null,
""
],
"BOSH Service URL:": [
null,
""
],
"Connected": [
null,
""
],
"Disconnected": [
null,
""
],
"Error": [
null,
""
],
"Connecting": [
null,
""
],
"Connection Failed": [
null,
""
],
"Authenticating": [
null,
""
],
"Authentication Failed": [
null,
""
],
"Disconnecting": [
null,
""
],
"Attached": [
null,
""
],
"Online Contacts": [
null,
""
]
}
}
});
return factory(hu);
});
}(this, function (hu) {
return hu;
}));

View File

@ -1,5 +1,5 @@
(function (root, factory) {
var it = new Jed({
var translations = {
"domain": "converse",
"locale_data": {
"converse": {
@ -461,16 +461,16 @@
]
}
}
});
};
if (typeof define === 'function' && define.amd) {
define("it", ['jed'], function () {
return factory(it);
return factory(new Jed(translations));
});
} else {
if (!window.locales) {
window.locales = {};
}
window.locales.it = it;
window.locales.it = factory(new Jed(translations));
}
}(this, function (it) {
return it;

View File

@ -8,12 +8,11 @@
(function (root, factory) {
require.config({
paths: {
"jed": "Libraries/jed",
"jed": "components/jed/jed",
"af": "locale/af/LC_MESSAGES/af",
"en": "locale/en/LC_MESSAGES/en",
"es": "locale/es/LC_MESSAGES/es",
"de": "locale/de/LC_MESSAGES/de",
"hu": "locale/hu/LC_MESSAGES/hu",
"it": "locale/it/LC_MESSAGES/it",
"pt_BR": "locale/pt_BR/LC_MESSAGES/pt_BR"
}
@ -25,16 +24,14 @@
'en',
'es',
'de',
'hu',
"it",
"pt_BR"
], function (jed, af, en, es, de, hu, it, pt_BR) {
], function (jed, af, en, es, de, it, pt_BR) {
root.locales = {};
root.locales.af = af;
root.locales.en = en;
root.locales.es = es;
root.locales.de = de;
root.locales.hu = hu;
root.locales.it = it;
root.locales.pt_BR = pt_BR;
});

View File

@ -1,5 +1,5 @@
(function (root, factory) {
var pt_BR = new Jed({
var translations = {
"domain": "converse",
"locale_data": {
"converse": {
@ -461,16 +461,16 @@
]
}
}
});
};
if (typeof define === 'function' && define.amd) {
define("pt_BR", ['jed'], function () {
return factory(pt_BR);
return factory(new Jed(translations));
});
} else {
if (!window.locales) {
window.locales = {};
}
window.locales.pt_BR = pt_BR;
window.locales.pt_BR = factory(new Jed(translations));
}
}(this, function (i18n) {
return i18n;

41
main.js
View File

@ -1,3 +1,42 @@
require(["jquery", "converse"], function($, converse) {
require.config({
paths: {
"jquery": "components/jquery/jquery",
"locales": "locale/locales",
"sjcl": "components/sjcl/sjcl",
"tinysort": "components/tinysort/src/jquery.tinysort",
"underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone",
"localstorage": "components/backbone.localStorage/backbone.localStorage",
"strophe": "components/strophe/strophe",
"strophe.muc": "components/strophe.muc/index",
"strophe.roster": "components/strophe.roster/index",
"strophe.vcard": "components/strophe.vcard/index",
"strophe.disco": "components/strophe.disco/index"
},
// define module dependencies for modules not using define
shim: {
'backbone': {
//These script dependencies should be loaded before loading
//backbone.js
deps: [
'underscore',
'jquery'
],
//Once loaded, use the global 'Backbone' as the
//module value.
exports: 'Backbone'
},
'tinysort': { deps: ['jquery'] },
'strophe': { deps: ['jquery'] },
'underscore': { exports: '_' },
'strophe.muc': { deps: ['strophe', 'jquery'] },
'strophe.roster': { deps: ['strophe'] },
'strophe.vcard': { deps: ['strophe'] },
'strophe.disco': { deps: ['strophe'] }
}
});
require(["components/requirejs/require", "jquery", "converse"], function(require, $, converse) {
window.converse = converse;
});

View File

@ -8,17 +8,17 @@
<link rel="stylesheet" type="text/css" media="screen" href="converse.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript" src="Libraries/strophe.js"></script>
<script type="text/javascript" src="Libraries/strophe.roster.js"></script>
<script type="text/javascript" src="Libraries/strophe.muc.js"></script>
<script type="text/javascript" src="Libraries/strophe.vcard.js"></script>
<script type="text/javascript" src="Libraries/strophe.disco.js"></script>
<script type="text/javascript" src="Libraries/underscore.js"></script>
<script type="text/javascript" src="Libraries/backbone.js"></script>
<script type="text/javascript" src="Libraries/backbone.localStorage.js"></script>
<script type="text/javascript" src="Libraries/sjcl.js"></script>
<script type="text/javascript" src="Libraries/jquery.tinysort.js"></script>
<script type="text/javascript" src="Libraries/jed.js"></script>
<script type="text/javascript" src="components/strophe/strophe.js"></script>
<script type="text/javascript" src="components/strophe.roster/index.js"></script>
<script type="text/javascript" src="components/strophe.muc/index.js"></script>
<script type="text/javascript" src="components/strophe.vcard/index.js"></script>
<script type="text/javascript" src="components/strophe.disco/index.js"></script>
<script type="text/javascript" src="components/underscore/underscore.js"></script>
<script type="text/javascript" src="components/backbone//backbone.js"></script>
<script type="text/javascript" src="components/backbone.localStorage/backbone.localStorage.js"></script>
<script type="text/javascript" src="components/sjcl/sjcl.js"></script>
<script type="text/javascript" src="components/tinysort/src/jquery.tinysort.js"></script>
<script type="text/javascript" src="components/jed/jed.js"></script>
<script type="text/javascript" src="locale/en/LC_MESSAGES/en.js"></script>
<script type="text/javascript" src="converse.js"></script>
<title>Converse.js</title>

View File

@ -29,6 +29,11 @@
"devDependencies": {
"grunt-cli": "~0.1.9",
"grunt": "~0.4.1",
"grunt-contrib-jshint": "~0.6.0"
"grunt-contrib-jshint": "~0.6.0",
"phantomjs": "~1.9.1-0",
"jasmine-reporters": "~0.2.1",
"bower": "~1.0.0",
"grunt-contrib-requirejs": "~0.4.1",
"grunt-contrib-cssmin": "~0.6.1"
}
}

115
test_minified.html Normal file
View File

@ -0,0 +1,115 @@
<!doctype html public "-//w3c//dtd html 4.01 transitional//en"
"http://www.w3.org/tr/html4/loose.dtd">
<html>
<head>
<title>converse.js tests for minified files</title>
<meta name="description" content="converse.js: open source browser-based instant messaging" />
<link rel="shortcut icon" type="image/png" href="components/jasmine/images/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="components/jasmine/src/html/jasmine.css">
<link rel="stylesheet" type="text/css" media="screen" href="stylesheets/stylesheet.css">
<link rel="stylesheet" type="text/css" media="screen" href="converse-0.5.0.min.css">
<script src="converse-0.5.0.min.js"></script>
<script src="components/jasmine/lib/jasmine-core/jasmine.js"></script>
<script src="components/jasmine/lib/jasmine-core/jasmine-html.js"></script>
<script src="node_modules/jasmine-reporters/src/jasmine.console_reporter.js"></script>
<script src="node_modules/jasmine-reporters/src/jasmine.junit_reporter.js"></script>
</head>
<body>
<div id="header_wrap" class="outer">
<header class="inner">
<h1 id="project_title"><a href="http://conversejs.org">converse.js</a></h1>
<h2 id="project_tagline">tests</h2>
</header>
</div>
<div id="chatpanel">
<div id="collective-xmpp-chat-data"></div>
<div id="toggle-controlbox">
<a href="#" class="chat toggle-online-users">
<span class="conn-feedback">click here to chat</span> <strong style="display: none" id="online-count">(0)</strong>
</a>
</div>
</div>
<script>
require([
'converse',
"spec/MainSpec",
"spec/ChatRoomSpec"],
function (converse) {
var mock_connection = {
'muc': {
'listRooms': function () {},
'join': function () {},
'leave': function () {},
'removeRoom': function () {},
'rooms': {}
},
'jid': 'dummy@localhost',
'addHandler': function (handler, ns, name, type, id, from, options) {
return function () {};
},
'send': function () {},
'roster': {
'add': function () {},
'authorize': function () {},
'unauthorize': function () {},
'get': function () {},
'subscribe': function () {},
'registerCallback': function () {}
},
'vcard': {
'get': function (callback, jid) {
var firstname, lastname;
if (!jid) {
jid = 'dummy@localhost';
firstname = 'Max';
lastname = 'Mustermann';
} else {
var name = jid.split('@')[0].replace('.', ' ').split(' ');
firstname = name[0].charAt(0).toUpperCase()+name[0].slice(1);
lastname = name[1].charAt(0).toUpperCase()+name[1].slice(1);
}
var fullname = firstname+' '+lastname;
var vcard = $iq().c('vCard').c('FN').t(fullname);
callback(vcard.tree());
}
},
'disco': {
'info': function () {},
'items': function () {}
}
};
// Set up converse.js
window.localStorage.clear();
converse.initialize({
prebind: false,
xhr_user_search: false,
auto_subscribe: false,
animate: false
});
converse.onConnected(mock_connection);
// Jasmine stuff
var jasmineEnv = jasmine.getEnv();
if (/PhantomJS/.test(navigator.userAgent)) {
jasmineEnv.addReporter(new jasmine.TrivialReporter());
jasmineEnv.addReporter(new jasmine.JUnitXmlReporter('./test-reports/'));
jasmineEnv.addReporter(new jasmine.ConsoleReporter());
jasmineEnv.updateInterval = 0;
} else {
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.addReporter(new jasmine.ConsoleReporter());
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
jasmineEnv.updateInterval = 200;
}
jasmineEnv.execute();
});
</script>
</body>
</html>

View File

@ -4,13 +4,11 @@
<head>
<title>Converse.js Tests</title>
<meta name="description" content="Converse.js: Open Source Browser-Based Instant Messaging" />
<link rel="shortcut icon" type="image/png" href="Libraries/jasmine-1.3.1/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="Libraries/jasmine-1.3.1/jasmine.css">
<script type="text/javascript" src="Libraries/jasmine-1.3.1/jasmine.js"></script>
<script type="text/javascript" src="Libraries/jasmine-1.3.1/jasmine-html.js"></script>
<link rel="shortcut icon" type="image/png" href="components/jasmine/images/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="components/jasmine/src/html/jasmine.css">
<link rel="stylesheet" type="text/css" media="screen" href="stylesheets/stylesheet.css">
<link rel="stylesheet" type="text/css" media="screen" href="converse.css">
<script data-main="tests_main" src="Libraries/require-jquery.js"></script>
<script data-main="tests_main" src="components/requirejs/require.js"></script>
</head>
<body>

View File

@ -1,4 +1,72 @@
require(["jquery", "converse", "mock", "spec/MainSpec", "spec/ChatRoomSpec"], function($, converse, mock_connection) {
require.config({
paths: {
"jquery": "components/jquery/jquery",
"locales": "locale/locales",
"sjcl": "components/sjcl/sjcl",
"tinysort": "components/tinysort/src/jquery.tinysort",
"underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone",
"localstorage": "components/backbone.localStorage/backbone.localStorage",
"strophe": "components/strophe/strophe",
"strophe.muc": "components/strophe.muc/index",
"strophe.roster": "components/strophe.roster/index",
"strophe.vcard": "components/strophe.vcard/index",
"strophe.disco": "components/strophe.disco/index",
// Extra test dependencies
"jasmine": "components/jasmine/lib/jasmine-core/jasmine",
"jasmine-html": "components/jasmine/lib/jasmine-core/jasmine-html",
"jasmine-console-reporter": "node_modules/jasmine-reporters/src/jasmine.console_reporter",
"jasmine-junit-reporter": "node_modules/jasmine-reporters/src/jasmine.junit_reporter"
},
// define module dependencies for modules not using define
shim: {
'backbone': {
//These script dependencies should be loaded before loading
//backbone.js
deps: [
'underscore',
'jquery'
],
//Once loaded, use the global 'Backbone' as the
//module value.
exports: 'Backbone'
},
'tinysort': { deps: ['jquery'] },
'strophe': { deps: ['jquery'] },
'underscore': { exports: '_' },
'strophe.muc': { deps: ['strophe', 'jquery'] },
'strophe.roster': { deps: ['strophe'] },
'strophe.vcard': { deps: ['strophe'] },
'strophe.disco': { deps: ['strophe'] },
// Extra test dependencies
'jasmine-html': {
deps: ['jasmine'],
exports: 'jasmine'
},
'jasmine-console-reporter': {
deps: ['jasmine-html'],
exports: 'jasmine'
},
'jasmine-junit-reporter': {
deps: ['jasmine-html'],
exports: 'jasmine'
}
}
});
require([
"jquery",
"converse",
"mock",
"jasmine-html",
"jasmine-console-reporter",
"jasmine-junit-reporter",
"spec/MainSpec",
"spec/ChatRoomSpec"
], function($, converse, mock_connection, jasmine) {
// Set up converse.js
window.localStorage.clear();
converse.initialize({
@ -11,11 +79,19 @@ require(["jquery", "converse", "mock", "spec/MainSpec", "spec/ChatRoomSpec"], fu
// Jasmine stuff
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 250;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
if (/PhantomJS/.test(navigator.userAgent)) {
jasmineEnv.addReporter(new jasmine.TrivialReporter());
jasmineEnv.addReporter(new jasmine.JUnitXmlReporter('./test-reports/'));
jasmineEnv.addReporter(new jasmine.ConsoleReporter());
jasmineEnv.updateInterval = 0;
} else {
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.addReporter(new jasmine.ConsoleReporter());
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
jasmineEnv.updateInterval = 200;
}
jasmineEnv.execute();
});