Merge pull request #701 from cweider/modules

Reuse NPM Modules
This commit is contained in:
John McLear 2012-09-11 07:24:55 -07:00
commit 2d274820ba
11 changed files with 59 additions and 2395 deletions

View file

@ -35,14 +35,35 @@ var ROOT_DIR = path.normalize(__dirname + "/../../static/");
var TAR_PATH = path.join(__dirname, 'tar.json');
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.
var LIBRARY_PREFIX = 'ep_etherpad-lite/static/js';
exports.tar = {};
function prefixLocalLibraryPath(path) {
if (path.charAt(0) == '$') {
return path.slice(1);
} else {
return LIBRARY_PREFIX + '/' + path;
}
}
for (var key in tar) {
exports.tar[LIBRARY_PREFIX + '/' + key] =
tar[key].map(function (p) {return LIBRARY_PREFIX + '/' + p}).concat(
tar[key].map(function (p) {
return LIBRARY_PREFIX + '/' + p.replace(/\.js$/, '')
exports.tar[prefixLocalLibraryPath(key)] =
tar[key].map(prefixLocalLibraryPath).concat(
tar[key].map(prefixLocalLibraryPath).map(function (p) {
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;
}
/* Handle static files for plugins:
/* Handle static files for plugins/libraries:
paths like "plugins/ep_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
*/
var match = filename.match(/^plugins\/([^\/]+)\/static\/(.*)/);
var match = filename.match(/^plugins\/([^\/]+)(\/(?:(static\/.*)|.*))?$/);
if (match) {
var pluginName = match[1];
var resourcePath = match[2];
var plugin = plugins.plugins[pluginName];
if (plugin) {
var library = match[1];
var libraryPath = match[2] || '';
if (plugins.plugins[library] && match[3]) {
var plugin = plugins.plugins[library];
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?)
} 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 {
callback(error);
}
} else {
} else if (stats.isFile()) {
callback(null, stats.mtime.getTime(), true);
} else {
callback(null, stats.mtime.getTime(), false);
}
});
}

View file

@ -14,6 +14,7 @@
, "pad_savedrevs.js"
, "pad_connectionstatus.js"
, "chat.js"
, "$tinycon/tinycon.js"
, "excanvas.js"
, "farbtastic.js"
]
@ -48,6 +49,7 @@
, "cssmanager.js"
, "colorutils.js"
, "undomodule.js"
, "$unorm.js"
, "contentcollector.js"
, "changesettracker.js"
, "linestylefilter.js"
@ -58,12 +60,16 @@
"ace2_common.js"
, "jquery.js"
, "rjquery.js"
, "$async.js"
, "$async/lib/async.js"
, "underscore.js"
, "$underscore.js"
, "$underscore/underscore.js"
, "security.js"
, "$security.js"
, "json2.js"
, "pluginfw/plugins.js"
, "pluginfw/hooks.js"
, "pluginfw/async.js"
, "pluginfw/parent_require.js"
]
}

View file

@ -30,7 +30,10 @@
"graceful-fs" : "1.1.5",
"slide" : "1.1.3",
"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" },
"devDependencies": {

View file

@ -23,7 +23,7 @@
var padutils = require('./pad_utils').padutils;
var padcookie = require('./pad_cookie').padcookie;
require('./tinycon');
var Tinycon = require('tinycon/tinycon');
var chat = (function()
{

View file

@ -25,7 +25,7 @@
var _MAX_LIST_LEVEL = 8;
var UNorm = require('./unorm');
var UNorm = require('unorm');
var Changeset = require('./Changeset');
var hooks = require('./pluginfw/hooks');
var _ = require('./underscore');

View file

@ -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);
}
};
}());

View file

@ -1,14 +1,6 @@
var plugins = require("ep_etherpad-lite/static/js/pluginfw/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 plugins = require("./plugins");
var async = require("async");
var _ = require("underscore");
}
exports.bubbleExceptions = true

View file

@ -14,41 +14,4 @@
* limitations under the License.
*/
var HTML_ENTITY_MAP = {
'&': '&amp;'
, '<': '&lt;'
, '>': '&gt;'
, '"': '&quot;'
, "'": '&#x27;'
, '/': '&#x2F;'
};
// 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;
module.exports = require('security');

View file

@ -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