mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-02-01 03:12:42 +01:00
commit
2d274820ba
11 changed files with 59 additions and 2395 deletions
|
@ -35,14 +35,35 @@ var ROOT_DIR = path.normalize(__dirname + "/../../static/");
|
||||||
var TAR_PATH = path.join(__dirname, 'tar.json');
|
var TAR_PATH = path.join(__dirname, 'tar.json');
|
||||||
var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8'));
|
var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8'));
|
||||||
|
|
||||||
|
|
||||||
|
var LIBRARY_WHITELIST = [
|
||||||
|
'async'
|
||||||
|
, 'security'
|
||||||
|
, 'tinycon'
|
||||||
|
, 'underscore'
|
||||||
|
, 'unorm'
|
||||||
|
];
|
||||||
|
|
||||||
// Rewrite tar to include modules with no extensions and proper rooted paths.
|
// Rewrite tar to include modules with no extensions and proper rooted paths.
|
||||||
var LIBRARY_PREFIX = 'ep_etherpad-lite/static/js';
|
var LIBRARY_PREFIX = 'ep_etherpad-lite/static/js';
|
||||||
exports.tar = {};
|
exports.tar = {};
|
||||||
|
function prefixLocalLibraryPath(path) {
|
||||||
|
if (path.charAt(0) == '$') {
|
||||||
|
return path.slice(1);
|
||||||
|
} else {
|
||||||
|
return LIBRARY_PREFIX + '/' + path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var key in tar) {
|
for (var key in tar) {
|
||||||
exports.tar[LIBRARY_PREFIX + '/' + key] =
|
exports.tar[prefixLocalLibraryPath(key)] =
|
||||||
tar[key].map(function (p) {return LIBRARY_PREFIX + '/' + p}).concat(
|
tar[key].map(prefixLocalLibraryPath).concat(
|
||||||
tar[key].map(function (p) {
|
tar[key].map(prefixLocalLibraryPath).map(function (p) {
|
||||||
return LIBRARY_PREFIX + '/' + p.replace(/\.js$/, '')
|
return p.replace(/\.js$/, '');
|
||||||
|
})
|
||||||
|
).concat(
|
||||||
|
tar[key].map(prefixLocalLibraryPath).map(function (p) {
|
||||||
|
return p.replace(/\.js$/, '') + '/index.js';
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -131,20 +152,26 @@ function minify(req, res, next)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle static files for plugins:
|
/* Handle static files for plugins/libraries:
|
||||||
paths like "plugins/ep_myplugin/static/js/test.js"
|
paths like "plugins/ep_myplugin/static/js/test.js"
|
||||||
are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js,
|
are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js,
|
||||||
commonly ETHERPAD_ROOT/node_modules/ep_myplugin/static/js/test.js
|
commonly ETHERPAD_ROOT/node_modules/ep_myplugin/static/js/test.js
|
||||||
*/
|
*/
|
||||||
var match = filename.match(/^plugins\/([^\/]+)\/static\/(.*)/);
|
var match = filename.match(/^plugins\/([^\/]+)(\/(?:(static\/.*)|.*))?$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
var pluginName = match[1];
|
var library = match[1];
|
||||||
var resourcePath = match[2];
|
var libraryPath = match[2] || '';
|
||||||
var plugin = plugins.plugins[pluginName];
|
|
||||||
if (plugin) {
|
if (plugins.plugins[library] && match[3]) {
|
||||||
|
var plugin = plugins.plugins[library];
|
||||||
var pluginPath = plugin.package.realPath;
|
var pluginPath = plugin.package.realPath;
|
||||||
filename = path.relative(ROOT_DIR, pluginPath + '/static/' + resourcePath);
|
filename = path.relative(ROOT_DIR, pluginPath + libraryPath);
|
||||||
filename = filename.replace(/\\/g, '/'); // Windows (safe generally?)
|
filename = filename.replace(/\\/g, '/'); // Windows (safe generally?)
|
||||||
|
} else if (LIBRARY_WHITELIST.indexOf(library) != -1) {
|
||||||
|
// Go straight into node_modules
|
||||||
|
// Avoid `require.resolve()`, since 'mustache' and 'mustache/index.js'
|
||||||
|
// would end up resolving to logically distinct resources.
|
||||||
|
filename = '../node_modules/' + library + libraryPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,8 +312,10 @@ function statFile(filename, callback) {
|
||||||
} else {
|
} else {
|
||||||
callback(error);
|
callback(error);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (stats.isFile()) {
|
||||||
callback(null, stats.mtime.getTime(), true);
|
callback(null, stats.mtime.getTime(), true);
|
||||||
|
} else {
|
||||||
|
callback(null, stats.mtime.getTime(), false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
, "pad_savedrevs.js"
|
, "pad_savedrevs.js"
|
||||||
, "pad_connectionstatus.js"
|
, "pad_connectionstatus.js"
|
||||||
, "chat.js"
|
, "chat.js"
|
||||||
|
, "$tinycon/tinycon.js"
|
||||||
, "excanvas.js"
|
, "excanvas.js"
|
||||||
, "farbtastic.js"
|
, "farbtastic.js"
|
||||||
]
|
]
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
, "cssmanager.js"
|
, "cssmanager.js"
|
||||||
, "colorutils.js"
|
, "colorutils.js"
|
||||||
, "undomodule.js"
|
, "undomodule.js"
|
||||||
|
, "$unorm.js"
|
||||||
, "contentcollector.js"
|
, "contentcollector.js"
|
||||||
, "changesettracker.js"
|
, "changesettracker.js"
|
||||||
, "linestylefilter.js"
|
, "linestylefilter.js"
|
||||||
|
@ -58,12 +60,16 @@
|
||||||
"ace2_common.js"
|
"ace2_common.js"
|
||||||
, "jquery.js"
|
, "jquery.js"
|
||||||
, "rjquery.js"
|
, "rjquery.js"
|
||||||
|
, "$async.js"
|
||||||
|
, "$async/lib/async.js"
|
||||||
, "underscore.js"
|
, "underscore.js"
|
||||||
|
, "$underscore.js"
|
||||||
|
, "$underscore/underscore.js"
|
||||||
, "security.js"
|
, "security.js"
|
||||||
|
, "$security.js"
|
||||||
, "json2.js"
|
, "json2.js"
|
||||||
, "pluginfw/plugins.js"
|
, "pluginfw/plugins.js"
|
||||||
, "pluginfw/hooks.js"
|
, "pluginfw/hooks.js"
|
||||||
, "pluginfw/async.js"
|
|
||||||
, "pluginfw/parent_require.js"
|
, "pluginfw/parent_require.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,10 @@
|
||||||
"graceful-fs" : "1.1.5",
|
"graceful-fs" : "1.1.5",
|
||||||
"slide" : "1.1.3",
|
"slide" : "1.1.3",
|
||||||
"semver" : "1.0.13",
|
"semver" : "1.0.13",
|
||||||
"underscore" : "1.3.1"
|
"security" : "1.0.0",
|
||||||
|
"tinycon" : "0.0.1",
|
||||||
|
"underscore" : "1.3.1",
|
||||||
|
"unorm" : "1.0.0"
|
||||||
},
|
},
|
||||||
"bin": { "etherpad-lite": "./node/server.js" },
|
"bin": { "etherpad-lite": "./node/server.js" },
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
var padutils = require('./pad_utils').padutils;
|
var padutils = require('./pad_utils').padutils;
|
||||||
var padcookie = require('./pad_cookie').padcookie;
|
var padcookie = require('./pad_cookie').padcookie;
|
||||||
|
|
||||||
require('./tinycon');
|
var Tinycon = require('tinycon/tinycon');
|
||||||
|
|
||||||
var chat = (function()
|
var chat = (function()
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
var _MAX_LIST_LEVEL = 8;
|
var _MAX_LIST_LEVEL = 8;
|
||||||
|
|
||||||
var UNorm = require('./unorm');
|
var UNorm = require('unorm');
|
||||||
var Changeset = require('./Changeset');
|
var Changeset = require('./Changeset');
|
||||||
var hooks = require('./pluginfw/hooks');
|
var hooks = require('./pluginfw/hooks');
|
||||||
var _ = require('./underscore');
|
var _ = require('./underscore');
|
||||||
|
|
|
@ -1,690 +0,0 @@
|
||||||
/*global setTimeout: false, console: false */
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
var async = {};
|
|
||||||
|
|
||||||
// global on the server, window in the browser
|
|
||||||
var root = this,
|
|
||||||
previous_async = root.async;
|
|
||||||
|
|
||||||
if (typeof module !== 'undefined' && module.exports) {
|
|
||||||
module.exports = async;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
root.async = async;
|
|
||||||
}
|
|
||||||
|
|
||||||
async.noConflict = function () {
|
|
||||||
root.async = previous_async;
|
|
||||||
return async;
|
|
||||||
};
|
|
||||||
|
|
||||||
//// cross-browser compatiblity functions ////
|
|
||||||
|
|
||||||
var _forEach = function (arr, iterator) {
|
|
||||||
if (arr.forEach) {
|
|
||||||
return arr.forEach(iterator);
|
|
||||||
}
|
|
||||||
for (var i = 0; i < arr.length; i += 1) {
|
|
||||||
iterator(arr[i], i, arr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var _map = function (arr, iterator) {
|
|
||||||
if (arr.map) {
|
|
||||||
return arr.map(iterator);
|
|
||||||
}
|
|
||||||
var results = [];
|
|
||||||
_forEach(arr, function (x, i, a) {
|
|
||||||
results.push(iterator(x, i, a));
|
|
||||||
});
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _reduce = function (arr, iterator, memo) {
|
|
||||||
if (arr.reduce) {
|
|
||||||
return arr.reduce(iterator, memo);
|
|
||||||
}
|
|
||||||
_forEach(arr, function (x, i, a) {
|
|
||||||
memo = iterator(memo, x, i, a);
|
|
||||||
});
|
|
||||||
return memo;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _keys = function (obj) {
|
|
||||||
if (Object.keys) {
|
|
||||||
return Object.keys(obj);
|
|
||||||
}
|
|
||||||
var keys = [];
|
|
||||||
for (var k in obj) {
|
|
||||||
if (obj.hasOwnProperty(k)) {
|
|
||||||
keys.push(k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return keys;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _indexOf = function (arr, item) {
|
|
||||||
if (arr.indexOf) {
|
|
||||||
return arr.indexOf(item);
|
|
||||||
}
|
|
||||||
for (var i = 0; i < arr.length; i += 1) {
|
|
||||||
if (arr[i] === item) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
//// exported async module functions ////
|
|
||||||
|
|
||||||
//// nextTick implementation with browser-compatible fallback ////
|
|
||||||
if (typeof process === 'undefined' || !(process.nextTick)) {
|
|
||||||
async.nextTick = function (fn) {
|
|
||||||
setTimeout(fn, 0);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
async.nextTick = process.nextTick;
|
|
||||||
}
|
|
||||||
|
|
||||||
async.forEach = function (arr, iterator, callback) {
|
|
||||||
if (!arr.length) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
var completed = 0;
|
|
||||||
_forEach(arr, function (x) {
|
|
||||||
iterator(x, function (err) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
callback = function () {};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
completed += 1;
|
|
||||||
if (completed === arr.length) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async.forEachSeries = function (arr, iterator, callback) {
|
|
||||||
if (!arr.length) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
var completed = 0;
|
|
||||||
var iterate = function () {
|
|
||||||
iterator(arr[completed], function (err) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
callback = function () {};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
completed += 1;
|
|
||||||
if (completed === arr.length) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
iterate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
iterate();
|
|
||||||
};
|
|
||||||
|
|
||||||
async.forEachLimit = function (arr, limit, iterator, callback) {
|
|
||||||
if (!arr.length || limit <= 0) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
var completed = 0;
|
|
||||||
var started = 0;
|
|
||||||
var running = 0;
|
|
||||||
|
|
||||||
(function replenish () {
|
|
||||||
if (completed === arr.length) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (running < limit && started < arr.length) {
|
|
||||||
iterator(arr[started], function (err) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
callback = function () {};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
completed += 1;
|
|
||||||
running -= 1;
|
|
||||||
if (completed === arr.length) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
replenish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
started += 1;
|
|
||||||
running += 1;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var doParallel = function (fn) {
|
|
||||||
return function () {
|
|
||||||
var args = Array.prototype.slice.call(arguments);
|
|
||||||
return fn.apply(null, [async.forEach].concat(args));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var doSeries = function (fn) {
|
|
||||||
return function () {
|
|
||||||
var args = Array.prototype.slice.call(arguments);
|
|
||||||
return fn.apply(null, [async.forEachSeries].concat(args));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var _asyncMap = function (eachfn, arr, iterator, callback) {
|
|
||||||
var results = [];
|
|
||||||
arr = _map(arr, function (x, i) {
|
|
||||||
return {index: i, value: x};
|
|
||||||
});
|
|
||||||
eachfn(arr, function (x, callback) {
|
|
||||||
iterator(x.value, function (err, v) {
|
|
||||||
results[x.index] = v;
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
callback(err, results);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
async.map = doParallel(_asyncMap);
|
|
||||||
async.mapSeries = doSeries(_asyncMap);
|
|
||||||
|
|
||||||
|
|
||||||
// reduce only has a series version, as doing reduce in parallel won't
|
|
||||||
// work in many situations.
|
|
||||||
async.reduce = function (arr, memo, iterator, callback) {
|
|
||||||
async.forEachSeries(arr, function (x, callback) {
|
|
||||||
iterator(memo, x, function (err, v) {
|
|
||||||
memo = v;
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
callback(err, memo);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// inject alias
|
|
||||||
async.inject = async.reduce;
|
|
||||||
// foldl alias
|
|
||||||
async.foldl = async.reduce;
|
|
||||||
|
|
||||||
async.reduceRight = function (arr, memo, iterator, callback) {
|
|
||||||
var reversed = _map(arr, function (x) {
|
|
||||||
return x;
|
|
||||||
}).reverse();
|
|
||||||
async.reduce(reversed, memo, iterator, callback);
|
|
||||||
};
|
|
||||||
// foldr alias
|
|
||||||
async.foldr = async.reduceRight;
|
|
||||||
|
|
||||||
var _filter = function (eachfn, arr, iterator, callback) {
|
|
||||||
var results = [];
|
|
||||||
arr = _map(arr, function (x, i) {
|
|
||||||
return {index: i, value: x};
|
|
||||||
});
|
|
||||||
eachfn(arr, function (x, callback) {
|
|
||||||
iterator(x.value, function (v) {
|
|
||||||
if (v) {
|
|
||||||
results.push(x);
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
callback(_map(results.sort(function (a, b) {
|
|
||||||
return a.index - b.index;
|
|
||||||
}), function (x) {
|
|
||||||
return x.value;
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
async.filter = doParallel(_filter);
|
|
||||||
async.filterSeries = doSeries(_filter);
|
|
||||||
// select alias
|
|
||||||
async.select = async.filter;
|
|
||||||
async.selectSeries = async.filterSeries;
|
|
||||||
|
|
||||||
var _reject = function (eachfn, arr, iterator, callback) {
|
|
||||||
var results = [];
|
|
||||||
arr = _map(arr, function (x, i) {
|
|
||||||
return {index: i, value: x};
|
|
||||||
});
|
|
||||||
eachfn(arr, function (x, callback) {
|
|
||||||
iterator(x.value, function (v) {
|
|
||||||
if (!v) {
|
|
||||||
results.push(x);
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
callback(_map(results.sort(function (a, b) {
|
|
||||||
return a.index - b.index;
|
|
||||||
}), function (x) {
|
|
||||||
return x.value;
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
async.reject = doParallel(_reject);
|
|
||||||
async.rejectSeries = doSeries(_reject);
|
|
||||||
|
|
||||||
var _detect = function (eachfn, arr, iterator, main_callback) {
|
|
||||||
eachfn(arr, function (x, callback) {
|
|
||||||
iterator(x, function (result) {
|
|
||||||
if (result) {
|
|
||||||
main_callback(x);
|
|
||||||
main_callback = function () {};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
main_callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
async.detect = doParallel(_detect);
|
|
||||||
async.detectSeries = doSeries(_detect);
|
|
||||||
|
|
||||||
async.some = function (arr, iterator, main_callback) {
|
|
||||||
async.forEach(arr, function (x, callback) {
|
|
||||||
iterator(x, function (v) {
|
|
||||||
if (v) {
|
|
||||||
main_callback(true);
|
|
||||||
main_callback = function () {};
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
main_callback(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// any alias
|
|
||||||
async.any = async.some;
|
|
||||||
|
|
||||||
async.every = function (arr, iterator, main_callback) {
|
|
||||||
async.forEach(arr, function (x, callback) {
|
|
||||||
iterator(x, function (v) {
|
|
||||||
if (!v) {
|
|
||||||
main_callback(false);
|
|
||||||
main_callback = function () {};
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
main_callback(true);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// all alias
|
|
||||||
async.all = async.every;
|
|
||||||
|
|
||||||
async.sortBy = function (arr, iterator, callback) {
|
|
||||||
async.map(arr, function (x, callback) {
|
|
||||||
iterator(x, function (err, criteria) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callback(null, {value: x, criteria: criteria});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, function (err, results) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var fn = function (left, right) {
|
|
||||||
var a = left.criteria, b = right.criteria;
|
|
||||||
return a < b ? -1 : a > b ? 1 : 0;
|
|
||||||
};
|
|
||||||
callback(null, _map(results.sort(fn), function (x) {
|
|
||||||
return x.value;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async.auto = function (tasks, callback) {
|
|
||||||
callback = callback || function () {};
|
|
||||||
var keys = _keys(tasks);
|
|
||||||
if (!keys.length) {
|
|
||||||
return callback(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
var results = {};
|
|
||||||
|
|
||||||
var listeners = [];
|
|
||||||
var addListener = function (fn) {
|
|
||||||
listeners.unshift(fn);
|
|
||||||
};
|
|
||||||
var removeListener = function (fn) {
|
|
||||||
for (var i = 0; i < listeners.length; i += 1) {
|
|
||||||
if (listeners[i] === fn) {
|
|
||||||
listeners.splice(i, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var taskComplete = function () {
|
|
||||||
_forEach(listeners, function (fn) {
|
|
||||||
fn();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
addListener(function () {
|
|
||||||
if (_keys(results).length === keys.length) {
|
|
||||||
callback(null, results);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_forEach(keys, function (k) {
|
|
||||||
var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
|
|
||||||
var taskCallback = function (err) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
// stop subsequent errors hitting callback multiple times
|
|
||||||
callback = function () {};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
if (args.length <= 1) {
|
|
||||||
args = args[0];
|
|
||||||
}
|
|
||||||
results[k] = args;
|
|
||||||
taskComplete();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
|
|
||||||
var ready = function () {
|
|
||||||
return _reduce(requires, function (a, x) {
|
|
||||||
return (a && results.hasOwnProperty(x));
|
|
||||||
}, true);
|
|
||||||
};
|
|
||||||
if (ready()) {
|
|
||||||
task[task.length - 1](taskCallback, results);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var listener = function () {
|
|
||||||
if (ready()) {
|
|
||||||
removeListener(listener);
|
|
||||||
task[task.length - 1](taskCallback, results);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
addListener(listener);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async.waterfall = function (tasks, callback) {
|
|
||||||
if (!tasks.length) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
callback = callback || function () {};
|
|
||||||
var wrapIterator = function (iterator) {
|
|
||||||
return function (err) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
callback = function () {};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
var next = iterator.next();
|
|
||||||
if (next) {
|
|
||||||
args.push(wrapIterator(next));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
args.push(callback);
|
|
||||||
}
|
|
||||||
async.nextTick(function () {
|
|
||||||
iterator.apply(null, args);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
wrapIterator(async.iterator(tasks))();
|
|
||||||
};
|
|
||||||
|
|
||||||
async.parallel = function (tasks, callback) {
|
|
||||||
callback = callback || function () {};
|
|
||||||
if (tasks.constructor === Array) {
|
|
||||||
async.map(tasks, function (fn, callback) {
|
|
||||||
if (fn) {
|
|
||||||
fn(function (err) {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
if (args.length <= 1) {
|
|
||||||
args = args[0];
|
|
||||||
}
|
|
||||||
callback.call(null, err, args);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var results = {};
|
|
||||||
async.forEach(_keys(tasks), function (k, callback) {
|
|
||||||
tasks[k](function (err) {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
if (args.length <= 1) {
|
|
||||||
args = args[0];
|
|
||||||
}
|
|
||||||
results[k] = args;
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
callback(err, results);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async.series = function (tasks, callback) {
|
|
||||||
callback = callback || function () {};
|
|
||||||
if (tasks.constructor === Array) {
|
|
||||||
async.mapSeries(tasks, function (fn, callback) {
|
|
||||||
if (fn) {
|
|
||||||
fn(function (err) {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
if (args.length <= 1) {
|
|
||||||
args = args[0];
|
|
||||||
}
|
|
||||||
callback.call(null, err, args);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var results = {};
|
|
||||||
async.forEachSeries(_keys(tasks), function (k, callback) {
|
|
||||||
tasks[k](function (err) {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
if (args.length <= 1) {
|
|
||||||
args = args[0];
|
|
||||||
}
|
|
||||||
results[k] = args;
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
callback(err, results);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async.iterator = function (tasks) {
|
|
||||||
var makeCallback = function (index) {
|
|
||||||
var fn = function () {
|
|
||||||
if (tasks.length) {
|
|
||||||
tasks[index].apply(null, arguments);
|
|
||||||
}
|
|
||||||
return fn.next();
|
|
||||||
};
|
|
||||||
fn.next = function () {
|
|
||||||
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
|
|
||||||
};
|
|
||||||
return fn;
|
|
||||||
};
|
|
||||||
return makeCallback(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
async.apply = function (fn) {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
return function () {
|
|
||||||
return fn.apply(
|
|
||||||
null, args.concat(Array.prototype.slice.call(arguments))
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var _concat = function (eachfn, arr, fn, callback) {
|
|
||||||
var r = [];
|
|
||||||
eachfn(arr, function (x, cb) {
|
|
||||||
fn(x, function (err, y) {
|
|
||||||
r = r.concat(y || []);
|
|
||||||
cb(err);
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
callback(err, r);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
async.concat = doParallel(_concat);
|
|
||||||
async.concatSeries = doSeries(_concat);
|
|
||||||
|
|
||||||
async.whilst = function (test, iterator, callback) {
|
|
||||||
if (test()) {
|
|
||||||
iterator(function (err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
async.whilst(test, iterator, callback);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async.until = function (test, iterator, callback) {
|
|
||||||
if (!test()) {
|
|
||||||
iterator(function (err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
async.until(test, iterator, callback);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async.queue = function (worker, concurrency) {
|
|
||||||
var workers = 0;
|
|
||||||
var q = {
|
|
||||||
tasks: [],
|
|
||||||
concurrency: concurrency,
|
|
||||||
saturated: null,
|
|
||||||
empty: null,
|
|
||||||
drain: null,
|
|
||||||
push: function (data, callback) {
|
|
||||||
q.tasks.push({data: data, callback: callback});
|
|
||||||
if(q.saturated && q.tasks.length == concurrency) q.saturated();
|
|
||||||
async.nextTick(q.process);
|
|
||||||
},
|
|
||||||
process: function () {
|
|
||||||
if (workers < q.concurrency && q.tasks.length) {
|
|
||||||
var task = q.tasks.shift();
|
|
||||||
if(q.empty && q.tasks.length == 0) q.empty();
|
|
||||||
workers += 1;
|
|
||||||
worker(task.data, function () {
|
|
||||||
workers -= 1;
|
|
||||||
if (task.callback) {
|
|
||||||
task.callback.apply(task, arguments);
|
|
||||||
}
|
|
||||||
if(q.drain && q.tasks.length + workers == 0) q.drain();
|
|
||||||
q.process();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
length: function () {
|
|
||||||
return q.tasks.length;
|
|
||||||
},
|
|
||||||
running: function () {
|
|
||||||
return workers;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return q;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _console_fn = function (name) {
|
|
||||||
return function (fn) {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
fn.apply(null, args.concat([function (err) {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
if (typeof console !== 'undefined') {
|
|
||||||
if (err) {
|
|
||||||
if (console.error) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (console[name]) {
|
|
||||||
_forEach(args, function (x) {
|
|
||||||
console[name](x);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
async.log = _console_fn('log');
|
|
||||||
async.dir = _console_fn('dir');
|
|
||||||
/*async.info = _console_fn('info');
|
|
||||||
async.warn = _console_fn('warn');
|
|
||||||
async.error = _console_fn('error');*/
|
|
||||||
|
|
||||||
async.memoize = function (fn, hasher) {
|
|
||||||
var memo = {};
|
|
||||||
var queues = {};
|
|
||||||
hasher = hasher || function (x) {
|
|
||||||
return x;
|
|
||||||
};
|
|
||||||
var memoized = function () {
|
|
||||||
var args = Array.prototype.slice.call(arguments);
|
|
||||||
var callback = args.pop();
|
|
||||||
var key = hasher.apply(null, args);
|
|
||||||
if (key in memo) {
|
|
||||||
callback.apply(null, memo[key]);
|
|
||||||
}
|
|
||||||
else if (key in queues) {
|
|
||||||
queues[key].push(callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
queues[key] = [callback];
|
|
||||||
fn.apply(null, args.concat([function () {
|
|
||||||
memo[key] = arguments;
|
|
||||||
var q = queues[key];
|
|
||||||
delete queues[key];
|
|
||||||
for (var i = 0, l = q.length; i < l; i++) {
|
|
||||||
q[i].apply(null, arguments);
|
|
||||||
}
|
|
||||||
}]));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
memoized.unmemoized = fn;
|
|
||||||
return memoized;
|
|
||||||
};
|
|
||||||
|
|
||||||
async.unmemoize = function (fn) {
|
|
||||||
return function () {
|
|
||||||
return (fn.unmemoized || fn).apply(null, arguments);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}());
|
|
|
@ -1,14 +1,6 @@
|
||||||
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
|
var plugins = require("./plugins");
|
||||||
var _;
|
|
||||||
|
|
||||||
/* FIXME: Ugly hack, in the future, use same code for server & client */
|
|
||||||
if (plugins.isClient) {
|
|
||||||
var async = require("ep_etherpad-lite/static/js/pluginfw/async");
|
|
||||||
var _ = require("ep_etherpad-lite/static/js/underscore");
|
|
||||||
} else {
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var _ = require("underscore");
|
var _ = require("underscore");
|
||||||
}
|
|
||||||
|
|
||||||
exports.bubbleExceptions = true
|
exports.bubbleExceptions = true
|
||||||
|
|
||||||
|
|
|
@ -14,41 +14,4 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var HTML_ENTITY_MAP = {
|
module.exports = require('security');
|
||||||
'&': '&'
|
|
||||||
, '<': '<'
|
|
||||||
, '>': '>'
|
|
||||||
, '"': '"'
|
|
||||||
, "'": '''
|
|
||||||
, '/': '/'
|
|
||||||
};
|
|
||||||
|
|
||||||
// OSWASP Guidlines: &, <, >, ", ' plus forward slash.
|
|
||||||
var HTML_CHARACTERS_EXPRESSION = /[&"'<>\/]/g;
|
|
||||||
function escapeHTML(text) {
|
|
||||||
return text && text.replace(HTML_CHARACTERS_EXPRESSION, function (c) {
|
|
||||||
return HTML_ENTITY_MAP[c] || c;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// OSWASP Guidlines: escape all non alphanumeric characters in ASCII space.
|
|
||||||
var HTML_ATTRIBUTE_CHARACTERS_EXPRESSION =
|
|
||||||
/[\x00-\x2F\x3A-\x40\5B-\x60\x7B-\xFF]/g;
|
|
||||||
function escapeHTMLAttribute(text) {
|
|
||||||
return text && text.replace(HTML_ATTRIBUTE_CHARACTERS_EXPRESSION, function (c) {
|
|
||||||
return "&#x" + ('00' + c.charCodeAt(0).toString(16)).slice(-2) + ";";
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// OSWASP Guidlines: escape all non alphanumeric characters in ASCII space.
|
|
||||||
var JAVASCRIPT_CHARACTERS_EXPRESSION =
|
|
||||||
/[\x00-\x2F\x3A-\x40\5B-\x60\x7B-\xFF]/g;
|
|
||||||
function escapeJavaScriptData(text) {
|
|
||||||
return text && text.replace(JAVASCRIPT_CHARACTERS_EXPRESSION, function (c) {
|
|
||||||
return "\\x" + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.escapeHTML = escapeHTML;
|
|
||||||
exports.escapeHTMLAttribute = escapeHTMLAttribute;
|
|
||||||
exports.escapeJavaScriptData = escapeJavaScriptData;
|
|
||||||
|
|
|
@ -1,237 +0,0 @@
|
||||||
/*!
|
|
||||||
* Tinycon - A small library for manipulating the Favicon
|
|
||||||
* Tom Moor, http://tommoor.com
|
|
||||||
* Copyright (c) 2012 Tom Moor
|
|
||||||
* MIT Licensed
|
|
||||||
* @version 0.2.6
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
var Tinycon = {};
|
|
||||||
var currentFavicon = null;
|
|
||||||
var originalFavicon = null;
|
|
||||||
var originalTitle = document.title;
|
|
||||||
var faviconImage = null;
|
|
||||||
var canvas = null;
|
|
||||||
var options = {};
|
|
||||||
var defaults = {
|
|
||||||
width: 7,
|
|
||||||
height: 9,
|
|
||||||
font: '10px arial',
|
|
||||||
colour: '#ffffff',
|
|
||||||
background: '#F03D25',
|
|
||||||
fallback: true
|
|
||||||
};
|
|
||||||
|
|
||||||
var ua = (function () {
|
|
||||||
var agent = navigator.userAgent.toLowerCase();
|
|
||||||
// New function has access to 'agent' via closure
|
|
||||||
return function (browser) {
|
|
||||||
return agent.indexOf(browser) !== -1;
|
|
||||||
};
|
|
||||||
}());
|
|
||||||
|
|
||||||
var browser = {
|
|
||||||
ie: ua('msie'),
|
|
||||||
chrome: ua('chrome'),
|
|
||||||
webkit: ua('chrome') || ua('safari'),
|
|
||||||
safari: ua('safari') && !ua('chrome'),
|
|
||||||
mozilla: ua('mozilla') && !ua('chrome') && !ua('safari')
|
|
||||||
};
|
|
||||||
|
|
||||||
// private methods
|
|
||||||
var getFaviconTag = function(){
|
|
||||||
|
|
||||||
var links = document.getElementsByTagName('link');
|
|
||||||
|
|
||||||
for(var i=0, len=links.length; i < len; i++) {
|
|
||||||
if ((links[i].getAttribute('rel') || '').match(/\bicon\b/)) {
|
|
||||||
return links[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
var removeFaviconTag = function(){
|
|
||||||
|
|
||||||
var links = document.getElementsByTagName('link');
|
|
||||||
var head = document.getElementsByTagName('head')[0];
|
|
||||||
|
|
||||||
for(var i=0, len=links.length; i < len; i++) {
|
|
||||||
var exists = (typeof(links[i]) !== 'undefined');
|
|
||||||
if (exists && links[i].getAttribute('rel') === 'icon') {
|
|
||||||
head.removeChild(links[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var getCurrentFavicon = function(){
|
|
||||||
|
|
||||||
if (!originalFavicon || !currentFavicon) {
|
|
||||||
var tag = getFaviconTag();
|
|
||||||
originalFavicon = currentFavicon = tag ? tag.getAttribute('href') : '/favicon.ico';
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentFavicon;
|
|
||||||
};
|
|
||||||
|
|
||||||
var getCanvas = function (){
|
|
||||||
|
|
||||||
if (!canvas) {
|
|
||||||
canvas = document.createElement("canvas");
|
|
||||||
canvas.width = 16;
|
|
||||||
canvas.height = 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
return canvas;
|
|
||||||
};
|
|
||||||
|
|
||||||
var setFaviconTag = function(url){
|
|
||||||
removeFaviconTag();
|
|
||||||
|
|
||||||
var link = document.createElement('link');
|
|
||||||
link.type = 'image/x-icon';
|
|
||||||
link.rel = 'icon';
|
|
||||||
link.href = url;
|
|
||||||
document.getElementsByTagName('head')[0].appendChild(link);
|
|
||||||
};
|
|
||||||
|
|
||||||
var log = function(message){
|
|
||||||
if (window.console) window.console.log(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
var drawFavicon = function(num, colour) {
|
|
||||||
|
|
||||||
// fallback to updating the browser title if unsupported
|
|
||||||
if (!getCanvas().getContext || browser.ie || browser.safari || options.fallback === 'force') {
|
|
||||||
return updateTitle(num);
|
|
||||||
}
|
|
||||||
|
|
||||||
var context = getCanvas().getContext("2d");
|
|
||||||
var colour = colour || '#000000';
|
|
||||||
var num = num || 0;
|
|
||||||
var src = getCurrentFavicon();
|
|
||||||
|
|
||||||
faviconImage = new Image();
|
|
||||||
faviconImage.onload = function() {
|
|
||||||
|
|
||||||
// clear canvas
|
|
||||||
context.clearRect(0, 0, 16, 16);
|
|
||||||
|
|
||||||
// draw original favicon
|
|
||||||
context.drawImage(faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, 16, 16);
|
|
||||||
|
|
||||||
// draw bubble over the top
|
|
||||||
if (num > 0) drawBubble(context, num, colour);
|
|
||||||
|
|
||||||
// refresh tag in page
|
|
||||||
refreshFavicon();
|
|
||||||
};
|
|
||||||
|
|
||||||
// allow cross origin resource requests if the image is not a data:uri
|
|
||||||
// as detailed here: https://github.com/mrdoob/three.js/issues/1305
|
|
||||||
if (!src.match(/^data/)) {
|
|
||||||
faviconImage.crossOrigin = 'anonymous';
|
|
||||||
}
|
|
||||||
|
|
||||||
faviconImage.src = src;
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateTitle = function(num) {
|
|
||||||
|
|
||||||
if (options.fallback) {
|
|
||||||
if (num > 0) {
|
|
||||||
document.title = '('+num+') ' + originalTitle;
|
|
||||||
} else {
|
|
||||||
document.title = originalTitle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var drawBubble = function(context, num, colour) {
|
|
||||||
|
|
||||||
// bubble needs to be larger for double digits
|
|
||||||
var len = (num+"").length-1;
|
|
||||||
var width = options.width + (6*len);
|
|
||||||
var w = 16-width;
|
|
||||||
var h = 16-options.height;
|
|
||||||
|
|
||||||
// webkit seems to render fonts lighter than firefox
|
|
||||||
context.font = (browser.webkit ? 'bold ' : '') + options.font;
|
|
||||||
context.fillStyle = options.background;
|
|
||||||
context.strokeStyle = options.background;
|
|
||||||
context.lineWidth = 1;
|
|
||||||
|
|
||||||
// bubble
|
|
||||||
context.fillRect(w,h,width-1,options.height);
|
|
||||||
|
|
||||||
// rounded left
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(w-0.5,h+1);
|
|
||||||
context.lineTo(w-0.5,15);
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
// rounded right
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(15.5,h+1);
|
|
||||||
context.lineTo(15.5,15);
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
// bottom shadow
|
|
||||||
context.beginPath();
|
|
||||||
context.strokeStyle = "rgba(0,0,0,0.3)";
|
|
||||||
context.moveTo(w,16);
|
|
||||||
context.lineTo(15,16);
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
// number
|
|
||||||
context.fillStyle = options.colour;
|
|
||||||
context.textAlign = "right";
|
|
||||||
context.textBaseline = "top";
|
|
||||||
|
|
||||||
// unfortunately webkit/mozilla are a pixel different in text positioning
|
|
||||||
context.fillText(num, 15, browser.mozilla ? 7 : 6);
|
|
||||||
};
|
|
||||||
|
|
||||||
var refreshFavicon = function(){
|
|
||||||
// check support
|
|
||||||
if (!getCanvas().getContext) return;
|
|
||||||
|
|
||||||
setFaviconTag(getCanvas().toDataURL());
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// public methods
|
|
||||||
Tinycon.setOptions = function(custom){
|
|
||||||
options = {};
|
|
||||||
|
|
||||||
for(var key in defaults){
|
|
||||||
options[key] = custom.hasOwnProperty(key) ? custom[key] : defaults[key];
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Tinycon.setImage = function(url){
|
|
||||||
currentFavicon = url;
|
|
||||||
refreshFavicon();
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Tinycon.setBubble = function(num, colour){
|
|
||||||
|
|
||||||
// validate
|
|
||||||
if(isNaN(parseFloat(num)) || !isFinite(num)) return log('Bubble must be a number');
|
|
||||||
|
|
||||||
drawFavicon(num, colour);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Tinycon.reset = function(){
|
|
||||||
Tinycon.setImage(originalFavicon);
|
|
||||||
};
|
|
||||||
|
|
||||||
Tinycon.setOptions(defaults);
|
|
||||||
window.Tinycon = Tinycon;
|
|
||||||
})();
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue