commonplace
Advanced tools
Comparing version 0.3.3 to 0.3.4
{ | ||
"name": "commonplace", | ||
"version": "0.3.3", | ||
"version": "0.3.4", | ||
"preferGlobal": true, | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -253,2 +253,7 @@ define('builder', | ||
var done = function(data) { | ||
if (signature.filters) { | ||
signature.filters.forEach(function(filterName) { | ||
data = env.filters[filterName](data); | ||
}); | ||
} | ||
document.getElementById(uid).innerHTML = data; | ||
@@ -255,0 +260,0 @@ }; |
@@ -1,2 +0,4 @@ | ||
define('cache', ['log', 'rewriters', 'storage'], function(log, rewriters, storage) { | ||
define('cache', | ||
['log', 'rewriters', 'settings', 'storage', 'user', 'utils', 'z'], | ||
function(log, rewriters, settings, storage, user, utils, z) { | ||
@@ -6,3 +8,92 @@ var console = log('cache'); | ||
var cache = {}; | ||
var cache_key = 'request_cache'; | ||
if (settings.offline_cache_enabled()) { | ||
cache = JSON.parse(storage.getItem(cache_key) || '{}'); | ||
flush_expired(); | ||
} | ||
// Persist the cache for whitelisted URLs. | ||
window.addEventListener('beforeunload', save, false); | ||
function get_ttl(url) { | ||
// Returns TTL for an API URL in microseconds. | ||
var path = utils.urlparse(url).pathname; | ||
if (path in settings.offline_cache_whitelist) { | ||
// Convert from seconds to microseconds. | ||
return settings.offline_cache_whitelist[path] * 1000; | ||
} | ||
return null; | ||
} | ||
function save() { | ||
if (!settings.offline_cache_enabled()) { | ||
return; | ||
} | ||
var cache_to_save = {}; | ||
Object.keys(cache).forEach(function (url) { | ||
// If there is a TTL assigned to this URL, then we can cache it. | ||
if (get_ttl(url) !== null) { | ||
cache_to_save[url] = cache[url]; | ||
} | ||
}); | ||
// Trigger an event to save the cache. (We do size checks to see if | ||
// the combined request+model cache is too large to persist.) | ||
z.doc.trigger('save_cache', cache_key); | ||
// Persist only if the data has changed. | ||
var cache_to_save_str = JSON.stringify(cache_to_save); | ||
if (storage.getItem(cache_key) !== cache_to_save_str) { | ||
storage.setItem(cache_key, cache_to_save_str); | ||
console.log('Persisting request cache'); | ||
} | ||
} | ||
function flush() { | ||
cache = {}; | ||
} | ||
function flush_signed() { | ||
// This gets called when a user logs out, so we remove any requests | ||
// with user data in them. | ||
// First, we remove every signed URL from the request cache. | ||
Object.keys(cache).forEach(function (url) { | ||
if (url.indexOf('_user=' + user.get_token()) !== -1) { | ||
console.log('Removing signed URL', url); | ||
delete cache[url]; | ||
} | ||
}); | ||
// Then, we persist the cache. | ||
save(); | ||
} | ||
function flush_expired() { | ||
// This gets called once when the page loads to purge any expired | ||
// persisted responses. | ||
var now = +new Date(); | ||
var time; | ||
var ttl = null; | ||
Object.keys(cache).forEach(function (url) { | ||
// Get the timestamp. | ||
time = cache[url].__time; | ||
// Get the TTL if this URL is allowed to be cached. | ||
ttl = get_ttl(url); | ||
// If the item is expired, remove it from the cache. | ||
if (!time || time + ttl <= now) { | ||
console.log('Removing expired URL', url); | ||
return delete cache[url]; | ||
} | ||
}); | ||
save(); | ||
} | ||
function has(key) { | ||
@@ -52,3 +143,3 @@ return key in cache; | ||
return get(key); | ||
} else if (key in storageKeys) { | ||
} else { | ||
var val = storage.getItem(persistentCachePrefix + key); | ||
@@ -64,5 +155,3 @@ set(key, val); | ||
bust: function(key) { | ||
if (key in storageKeys) { | ||
storage.removeItem(persistentCachePrefix + key); | ||
} | ||
storage.removeItem(persistentCachePrefix + key); | ||
bust(key); | ||
@@ -106,13 +195,16 @@ }, | ||
return { | ||
attemptRewrite: rewrite, | ||
bust: bust, | ||
cache: cache, | ||
flush: flush, | ||
flush_expired: flush_expired, | ||
flush_signed: flush_signed, | ||
get: get, | ||
get_ttl: get_ttl, | ||
has: has, | ||
get: get, | ||
set: set, | ||
bust: bust, | ||
persist: persistent, | ||
purge: purge, | ||
attemptRewrite: rewrite, | ||
raw: cache, | ||
persist: persistent | ||
set: set | ||
}; | ||
}); |
@@ -25,12 +25,11 @@ define('capabilities', [], function() { | ||
navigator.userAgent.indexOf('Android') === -1 && | ||
navigator.userAgent.indexOf('Mobile') !== -1, | ||
'persona': !!navigator.id, | ||
(navigator.userAgent.indexOf('Mobile') !== -1 || navigator.userAgent.indexOf('Tablet') !== -1), | ||
'phantom': navigator.userAgent.match(/Phantom/) // Don't use this if you can help it. | ||
}; | ||
static_caps.persona = !!navigator.id && !static_caps.phantom; | ||
static_caps.persona = function() { return (!!navigator.id || !!navigator.mozId) && !static_caps.phantom; }; | ||
// True if the login should inherit mobile behaviors such as allowUnverified. | ||
// The _shimmed check is for B2G where identity is native (not shimmed). | ||
static_caps.mobileLogin = static_caps.persona && (!navigator.id._shimmed || static_caps.firefoxAndroid); | ||
static_caps.mobileLogin = function() { return static_caps.persona() && (!navigator.id._shimmed || static_caps.firefoxAndroid); }; | ||
@@ -37,0 +36,0 @@ static_caps.device_type = function() { |
@@ -1,2 +0,2 @@ | ||
define('forms', ['jquery', 'z'], function($, z) { | ||
define('forms', ['z'], function(z) { | ||
@@ -8,4 +8,7 @@ function checkValid(form) { | ||
} | ||
z.body.on('change keyup paste', 'input, select, textarea', function(e) { | ||
// Note 'input' event is required for FF android (bug 977642) | ||
z.body.on('change input', 'input, textarea', function(e) { | ||
checkValid(e.target.form); | ||
}).on('change', 'select', function(e) { | ||
checkValid(e.target.form); | ||
}).on('loaded decloak', function() { | ||
@@ -12,0 +15,0 @@ $('form:not([novalidate])').each(function() { |
@@ -120,2 +120,3 @@ define('helpers', | ||
api: require('urls').api.url, | ||
apiHost: require('urls').api.host, | ||
apiParams: require('urls').api.params, | ||
@@ -131,6 +132,7 @@ anonApi: require('urls').api.unsigned.url, | ||
settings: require('settings'), | ||
capabilities: require('capabilities'), | ||
user: userobj, | ||
escape: utils.escape_, | ||
len: function(x) {return x ? x.length || 0 : 0;}, | ||
len: function(x) {return x.length;}, | ||
max: Math.max, | ||
@@ -137,0 +139,0 @@ min: Math.min, |
(function() { | ||
// This is a little misleading. If you're using the Marketplace this is likely | ||
// overridden below with body_langs. See bug 892741 for details. | ||
var languages = [ | ||
'bg', 'ca', 'cs', 'de', 'el', 'en-US', 'es', 'eu', 'fr', 'ga-IE', 'hr', | ||
'hu', 'it', 'ja', 'mk', 'nl', 'pl', 'pt-BR', 'ro', 'ru', 'sk', 'sr', | ||
'sr-Latn', 'tr', 'zh-TW', 'dbg' | ||
'bg', 'bn-BD', 'ca', 'cs', 'da', 'de', 'el', 'en-US', 'es', 'eu', 'fr', | ||
'ga-IE', 'hr', 'hu', 'it', 'ja', 'ko', 'mk', 'nb-NO', 'nl', 'pl', 'pt-BR', | ||
'ro', 'ru', 'sk', 'sq', 'sr', 'sr-Latn', 'tr', 'zh-CN', 'zh-TW', 'dbg' | ||
]; | ||
@@ -8,0 +10,0 @@ var body_langs; |
@@ -185,2 +185,23 @@ // Browser bundle of nunjucks 1.0.0 (slim, only works with precompiled templates) | ||
exports.toArray = function(obj) { | ||
return Array.prototype.slice.call(obj); | ||
}; | ||
exports.without = function(array) { | ||
var result = []; | ||
if (!array) { | ||
return result; | ||
} | ||
var index = -1, | ||
length = array.length, | ||
contains = exports.toArray(arguments).slice(1); | ||
while(++index < length) { | ||
if(contains.indexOf(array[index]) === -1) { | ||
result.push(array[index]); | ||
} | ||
} | ||
return result; | ||
}; | ||
exports.extend = function(obj, obj2) { | ||
@@ -237,2 +258,23 @@ for(var k in obj2) { | ||
exports.asyncParallel = function(funcs, done) { | ||
var count = funcs.length, | ||
result = new Array(count), | ||
current = 0; | ||
var makeNext = function(i) { | ||
return function(res) { | ||
result[i] = res; | ||
current += 1; | ||
if (current === count) { | ||
done(result); | ||
} | ||
}; | ||
}; | ||
for (var i = 0; i < count; i++) { | ||
funcs[i](makeNext(i)); | ||
} | ||
}; | ||
exports.asyncIter = function(arr, iter, cb) { | ||
@@ -275,2 +317,40 @@ var i = -1; | ||
if(!Array.prototype.indexOf) { | ||
Array.prototype.indexOf = function(array, searchElement /*, fromIndex */) { | ||
if (array === null) { | ||
throw new TypeError(); | ||
} | ||
var t = Object(array); | ||
var len = t.length >>> 0; | ||
if (len === 0) { | ||
return -1; | ||
} | ||
var n = 0; | ||
if (arguments.length > 2) { | ||
n = Number(arguments[2]); | ||
if (n != n) { // shortcut for verifying if it's NaN | ||
n = 0; | ||
} else if (n !== 0 && n !== Infinity && n !== -Infinity) { | ||
n = (n > 0 || -1) * Math.floor(Math.abs(n)); | ||
} | ||
} | ||
if (n >= len) { | ||
return -1; | ||
} | ||
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); | ||
for (; k < len; k++) { | ||
if (k in t && t[k] === searchElement) { | ||
return k; | ||
} | ||
} | ||
return -1; | ||
}; | ||
} | ||
if(!Array.prototype.map) { | ||
Array.prototype.map = function() { | ||
throw new Error("map is unimplemented for this js engine"); | ||
}; | ||
} | ||
exports.keys = function(obj) { | ||
@@ -503,2 +583,13 @@ if(Object.prototype.keys) { | ||
function callWrap(obj, name, args) { | ||
if(!obj) { | ||
throw new Error('Unable to call `' + name + '`, which is undefined or falsey'); | ||
} | ||
else if(typeof obj !== 'function') { | ||
throw new Error('Unable to call `' + name + '`, which is not a function'); | ||
} | ||
return obj.apply(this, args); | ||
} | ||
function contextOrFrameLookup(context, frame, name) { | ||
@@ -603,2 +694,3 @@ var val = frame.lookup(name); | ||
contextOrFrameLookup: contextOrFrameLookup, | ||
callWrap: callWrap, | ||
handleError: handleError, | ||
@@ -1306,3 +1398,7 @@ isArray: lib.isArray, | ||
} | ||
return e = new env.Environment(null, opts); | ||
var noWatch = 'watch' in opts ? !opts.watch : false; | ||
e = new env.Environment(null, opts); | ||
return e; | ||
}; | ||
@@ -1309,0 +1405,0 @@ |
@@ -67,3 +67,3 @@ define('log', ['storage', 'utils'], function(storage, utils) { | ||
tagged: function(newTag) { | ||
return logger(type, (tag ? tag + '][' : '') + newTag, onlog); | ||
return logger(type, tag + '][' + newTag, onlog); | ||
} | ||
@@ -70,0 +70,0 @@ }; |
define('login', | ||
['capabilities', 'defer', 'jquery', 'log', 'notification', 'settings', 'underscore', 'urls', 'user', 'requests', 'z'], | ||
function(capabilities, defer, $, log, notification, settings, _, urls, user, requests, z) { | ||
['cache', 'capabilities', 'defer', 'jquery', 'log', 'notification', 'settings', 'underscore', 'urls', 'user', 'utils', 'requests', 'z'], | ||
function(cache, capabilities, defer, $, log, notification, settings, _, urls, user, utils, requests, z) { | ||
@@ -28,5 +28,3 @@ var console = log('login'); | ||
requests.del(urls.api.url('logout')).done(function() { | ||
// Moved here from the onlogout callback for now until | ||
// https://github.com/mozilla/browserid/issues/3229 | ||
// gets fixed. | ||
cache.flush_signed(); | ||
user.clear_token(); | ||
@@ -36,3 +34,3 @@ z.body.removeClass('logged-in'); | ||
if (capabilities.persona) { | ||
if (capabilities.persona()) { | ||
console.log('Triggering Persona logout'); | ||
@@ -42,2 +40,5 @@ navigator.id.logout(); | ||
// Moved here from the onlogout callback for now until | ||
// https://github.com/mozilla/browserid/issues/3229 | ||
// gets fixed. | ||
if (!z.context.dont_reload_on_login) { | ||
@@ -78,3 +79,3 @@ require('views').reload().done(function(){ | ||
} | ||
if (capabilities.mobileLogin) { | ||
if (capabilities.mobileLogin()) { | ||
// On mobile we don't require new accounts to verify their email. | ||
@@ -88,6 +89,9 @@ // On desktop, we do. | ||
if (capabilities.persona) { | ||
console.log('Requesting login from Persona'); | ||
navigator.id.request(opt); | ||
} | ||
persona_loaded.done(function() { | ||
if (capabilities.persona()) { | ||
console.log('Requesting login from Persona'); | ||
navigator.id.request(opt); | ||
} | ||
}); | ||
return def.promise(); | ||
@@ -102,3 +106,3 @@ } | ||
audience: window.location.protocol + '//' + window.location.host, | ||
is_mobile: capabilities.mobileLogin | ||
is_mobile: capabilities.mobileLogin() | ||
}; | ||
@@ -155,22 +159,64 @@ | ||
var email = user.get_setting('email') || ''; | ||
if (email) { | ||
console.log('Detected user', email); | ||
} else { | ||
console.log('No previous user detected'); | ||
var persona_def = defer.Deferred(); | ||
var persona_loaded = persona_def.promise(); | ||
var persona_loading_start = +(new Date()); | ||
var persona_loading_time = 0; | ||
var persona_step = 25; // 25 milliseconds | ||
var GET = utils.getVars(); | ||
var persona_shim_included = $('script[src="' + settings.persona_shim_url + '"]').length; | ||
// If for some reason Zamboni got `?nativepersona=true` but we actually | ||
// don't have native Persona, then let's inject a script to load the shim. | ||
if (!persona_shim_included && !capabilities.persona()) { | ||
var s = document.createElement('script'); | ||
s.async = true; | ||
s.src = settings.persona_shim_url; | ||
document.body.appendChild(s); | ||
} | ||
if (capabilities.persona) { | ||
console.log('Calling navigator.id.watch'); | ||
navigator.id.watch({ | ||
loggedInUser: email, | ||
onlogin: gotVerifiedEmail, | ||
onlogout: function() { | ||
z.body.removeClass('logged-in'); | ||
z.page.trigger('reload_chrome').trigger('logout'); | ||
} | ||
var persona_interval = setInterval(function() { | ||
persona_loading_time = +(new Date()) - persona_loading_start; | ||
if (capabilities.persona()) { | ||
console.log('Persona loaded (' + persona_loading_time / 1000 + 's)'); | ||
persona_def.resolve(); | ||
clearInterval(persona_interval); | ||
} else if (persona_loading_time >= settings.persona_timeout) { | ||
console.error('Persona timeout (' + persona_loading_time / 1000 + 's)'); | ||
persona_def.reject(); | ||
clearInterval(persona_interval); | ||
} | ||
}, persona_step); | ||
persona_loaded.done(function() { | ||
// This lets us change the cursor for the "Sign in" link. | ||
z.body.addClass('persona-loaded'); | ||
var email = user.get_setting('email') || ''; | ||
if (email) { | ||
console.log('Detected user', email); | ||
} else { | ||
console.log('No previous user detected'); | ||
} | ||
if (capabilities.persona()) { | ||
console.log('Calling navigator.id.watch'); | ||
navigator.id.watch({ | ||
loggedInUser: email, | ||
onlogin: gotVerifiedEmail, | ||
onlogout: function() { | ||
z.body.removeClass('logged-in'); | ||
z.page.trigger('reload_chrome').trigger('logout'); | ||
} | ||
}); | ||
} | ||
}).fail(function() { | ||
notification.notification({ | ||
message: gettext('Persona cannot be reached. Try again later.') | ||
}); | ||
} | ||
}); | ||
return {login: startLogin}; | ||
}); |
@@ -1,2 +0,4 @@ | ||
define('models', ['defer', 'log', 'requests', 'settings', 'underscore'], function(defer, log, requests, settings, _) { | ||
define('models', | ||
['cache', 'defer', 'log', 'requests', 'settings', 'storage', 'underscore', 'z'], | ||
function(cache, defer, log, requests, settings, storage, _, z) { | ||
@@ -6,4 +8,60 @@ var console = log('model'); | ||
// {'type': {'<id>': object}} | ||
var cache_key = 'model_cache'; | ||
var data_store = {}; | ||
if (settings.offline_cache_enabled()) { | ||
data_store = JSON.parse(storage.getItem(cache_key) || '{}'); | ||
} | ||
// Persist the model cache. | ||
window.addEventListener('beforeunload', save, false); | ||
z.doc.on('saving_offline_cache', function (e, cache_key) { | ||
// Really, this should be an LRU cache but the builder has an | ||
// expectation that a hit to the request cache means that the models | ||
// have been casted and exist already in the model cache too. | ||
// | ||
// It gets too complicated having one LRU cache for the request cache | ||
// and then independent LRU caches for app, category, and collection | ||
// model caches. It's fine. It's fine. | ||
var data = { | ||
'request_cache': JSON.stringify(cache.cache), | ||
'model_cache': JSON.stringify(data_store) | ||
}; | ||
var size = (JSON.stringify(data.request_cache).length + | ||
JSON.stringify(data.model_cache).length); | ||
if (size >= settings.offline_cache_limit) { | ||
console.warn('Quota exceeded for request/model offline cache; ' + | ||
'flushing cache'); | ||
cache.flush(); | ||
flush(); | ||
storage.setItem(cache_key, data_store_str); | ||
} else { | ||
// Persist only if the data has changed. | ||
var data_store_str = data[cache_key]; | ||
if (storage.getItem(cache_key) !== data_store_str) { | ||
storage.setItem(cache_key, data_store_str); | ||
console.log('Persisting model cache'); | ||
} | ||
} | ||
}); | ||
function flush() { | ||
// Purge cache for every type of model. | ||
data_store = {}; | ||
} | ||
function save() { | ||
z.doc.trigger('save_cache', cache_key); | ||
// Persist only if the data has changed. | ||
var data_store_str = JSON.stringify(data_store); | ||
if (storage.getItem(cache_key) !== data_store_str) { | ||
storage.setItem(cache_key, data_store_str); | ||
console.log('Persisting model cache'); | ||
} | ||
} | ||
var prototypes = settings.model_prototypes; | ||
@@ -100,7 +158,9 @@ | ||
cast: cast, | ||
uncast: uncast, | ||
data_store: data_store, | ||
del: del, | ||
flush: flush, | ||
get: get, | ||
lookup: lookup, | ||
purge: purge, | ||
del: del | ||
uncast: uncast | ||
}; | ||
@@ -107,0 +167,0 @@ }; |
@@ -13,2 +13,3 @@ define('navigation', | ||
var initialized = false; | ||
var scrollTimer; | ||
@@ -25,5 +26,6 @@ function extract_nav_url(url) { | ||
// We can't use urlparams() because that only extends, not replaces. | ||
var used_params = _.pick(utils.querystring(url), settings.param_whitelist); | ||
// We can't use urlparams() because that only extends, not replaces. | ||
return utils.baseurl(url) + '?' + utils.urlencode(used_params); | ||
var queryParams = utils.urlencode(used_params); | ||
return utils.baseurl(url) + (queryParams.length ? '?' + queryParams : ''); | ||
} | ||
@@ -73,5 +75,13 @@ | ||
} | ||
console.log('Setting scroll to', top); | ||
window.scrollTo(0, top); | ||
// Introduce small delay to ensure content | ||
// is ready to scroll. (Bug 976466) | ||
if (scrollTimer) { | ||
window.clearTimeout(scrollTimer); | ||
} | ||
scrollTimer = window.setTimeout(function() { | ||
console.log('Setting scroll to', top); | ||
window.scrollTo(0, top); | ||
}, 250); | ||
// Clean the path's parameters. | ||
@@ -195,3 +205,3 @@ // /foo/bar?foo=bar&q=blah -> /foo/bar?q=blah | ||
z.doc.on('click', 'a', function(e) { | ||
z.body.on('click', 'a', function(e) { | ||
var href = this.getAttribute('href'); | ||
@@ -220,4 +230,9 @@ var $elm = $(this); | ||
if (state) { | ||
console.log('popstate navigate'); | ||
navigate(state.path, true, state); | ||
if (state.closeModalName) { | ||
console.log('popstate closing modal'); | ||
cleanupModal(state.closeModalName); | ||
} else { | ||
console.log('popstate navigate'); | ||
navigate(state.path, true, state); | ||
} | ||
} | ||
@@ -229,8 +244,35 @@ }).on('submit', 'form', function() { | ||
function modal(name) { | ||
console.log('Opening modal', name); | ||
stack[0].closeModalName = name; | ||
history.replaceState(stack[0], false, stack[0].path); | ||
history.pushState(null, name, '#' + name); | ||
var path = window.location.href + '#' + name; | ||
stack.unshift({path: path, type: 'modal', name: name}); | ||
} | ||
function cleanupModal(name) { | ||
stack.shift(); | ||
delete stack[0].closeModalName; | ||
z.win.trigger('closeModal', name); | ||
} | ||
function closeModal(name) { | ||
if (stack[0].type === 'modal' && stack[0].name === name) { | ||
console.log('Closing modal', name); | ||
history.back(); | ||
} else { | ||
console.log('Attempted to close modal', name, 'that was not open'); | ||
} | ||
} | ||
return { | ||
'back': back, | ||
'modal': modal, | ||
'closeModal': closeModal, | ||
'stack': function() {return stack;}, | ||
'navigationFilter': navigationFilter | ||
'navigationFilter': navigationFilter, | ||
'extract_nav_url': extract_nav_url | ||
}; | ||
}); |
define('requests', | ||
['cache', 'defer', 'log', 'utils'], | ||
function(cache, defer, log, utils) { | ||
['cache', 'defer', 'log', 'settings', 'utils'], | ||
function(cache, defer, log, settings, utils) { | ||
@@ -99,3 +99,12 @@ var console = log('req'); | ||
function get(url, nocache, persistent) { | ||
// During a single session, we never want to fetch the same URL more than | ||
// once. Because our persistent offline cache does XHRs in the background | ||
// to keep the cache fresh, we want to do that only once per session. In | ||
// order to do all this magic, we have to keep an array of all of the URLs | ||
// we hit per session. | ||
var urls_fetched = {}; | ||
function get(url, nocache) { | ||
var cache_offline = settings.offline_cache_enabled(); | ||
var cached; | ||
@@ -105,23 +114,40 @@ if (cache.has(url) && !nocache) { | ||
cached = cache.get(url); | ||
} else if (cache.persist.has(url) && persistent && !nocache) { | ||
console.log('GETing from persistent cache', url); | ||
cached = cache.persist.get(url); | ||
} | ||
var def_cached; | ||
if (cached) { | ||
return defer.Deferred() | ||
.resolve(cached) | ||
.promise({__cached: true}); | ||
def_cached = defer.Deferred() | ||
.resolve(cached) | ||
.promise({__cached: true}); | ||
if (!cache_offline || url in urls_fetched) { | ||
// If we don't need to make an XHR in the background to update | ||
// the cache, then let's bail now. | ||
return def_cached; | ||
} | ||
} | ||
return _get.apply(this, arguments, persistent); | ||
} | ||
function _get(url, nocache, persistent) { | ||
console.log('GETing', url); | ||
return ajax('GET', url).done(function(data, xhr) { | ||
urls_fetched[url] = null; | ||
var def_ajax = ajax('GET', url).done(function(data) { | ||
console.log('GOT', url); | ||
if (!nocache) { | ||
data.__time = +(new Date()); | ||
cache.set(url, data); | ||
if (persistent) cache.persist.set(url, data); | ||
} | ||
if (cached && cache_offline) { | ||
console.log('Updating request cache', url); | ||
} | ||
}); | ||
if (cached && cache_offline) { | ||
// If the response was cached, we still want to fire off the | ||
// AJAX request so the cache can get updated in the background, | ||
// but let's resolve this deferred with the cached response | ||
// so the request pool can get closed and the builder can render | ||
// the template for the `defer` block. | ||
return def_cached; | ||
} | ||
return def_ajax; | ||
} | ||
@@ -180,2 +206,3 @@ | ||
var closed = false; | ||
var failed = false; | ||
@@ -191,7 +218,3 @@ function finish() { | ||
// Resolve the deferred whenevs. | ||
if (window.setImmediate) { | ||
setImmediate(def.resolve); | ||
} else { | ||
setTimeout(def.resolve, 0); | ||
} | ||
setTimeout(failed ? def.reject : def.resolve, 0); | ||
} | ||
@@ -209,2 +232,5 @@ } | ||
requests.push(req); | ||
req.fail(function() { | ||
failed = true; | ||
}); | ||
req.always(function() { | ||
@@ -211,0 +237,0 @@ initiated--; |
define('urls', | ||
['format', 'routes_api', 'routes_api_args', 'settings', 'user', 'utils'], | ||
function(format, api_endpoints, api_args, settings, user) { | ||
['format', 'log', 'routes_api', 'routes_api_args', 'settings', 'user', 'utils'], | ||
function(format, log, api_endpoints, api_args, settings, user, utils) { | ||
var console = log('urls'); | ||
// The CDN URL is the same as the media URL but without the `/media/` path. | ||
if ('media_url' in settings) { | ||
var a = document.createElement('a'); | ||
a.href = settings.media_url; | ||
settings.cdn_url = a.protocol + '//' + a.host; | ||
console.log('Using settings.media_url: ' + settings.media_url); | ||
console.log('Changed settings.cdn_url: ' + settings.cdn_url); | ||
} else { | ||
settings.cdn_url = settings.api_url; | ||
console.log('Changed settings.cdn_url to settings.api_url: ' + settings.api_url); | ||
} | ||
var group_pattern = /\([^\)]+\)/; | ||
@@ -47,3 +61,3 @@ var optional_pattern = /(\(.*\)|\[.*\]|.)\?/g; | ||
_removeBlacklistedParams(args); | ||
return require('utils').urlparams(out, args); | ||
return utils.urlparams(out, args); | ||
}; | ||
@@ -57,3 +71,3 @@ } | ||
_removeBlacklistedParams(args); | ||
return require('utils').urlparams(out, args); | ||
return utils.urlparams(out, args); | ||
}; | ||
@@ -76,5 +90,8 @@ } | ||
} | ||
var url = settings.api_url + format.format(api_endpoints[endpoint], args || []); | ||
var path = format.format(api_endpoints[endpoint], args || []); | ||
var url = apiHost(path) + path; | ||
if (params) { | ||
return require('utils').urlparams(url, params); | ||
return utils.urlparams(url, params); | ||
} | ||
@@ -88,2 +105,12 @@ return url; | ||
function apiHost(path) { | ||
// If the API URL is already reversed, then here's where we determine | ||
// whether that URL gets served from the API or CDN. | ||
var host = settings.api_url; | ||
if (utils.baseurl(path) in settings.api_cdn_whitelist) { | ||
host = settings.cdn_url; | ||
} | ||
return host; | ||
} | ||
function media(path) { | ||
@@ -104,2 +131,3 @@ var media_url = settings.media_url; | ||
url: _userArgs(api), | ||
host: apiHost, | ||
params: _userArgs(apiParams), | ||
@@ -114,2 +142,3 @@ sign: _userArgs(function(url) {return url;}), | ||
url: api, | ||
host: apiHost, | ||
params: apiParams | ||
@@ -116,0 +145,0 @@ } |
@@ -36,3 +36,4 @@ define('utils', ['jquery', 'l10n', 'underscore'], function($, l10n, _) { | ||
.find('#' + $cc.data('for')) | ||
.on('keyup blur', _.throttle(function() {countChars(this, $cc);}, 250)) | ||
// Note 'input' event is need for FF android see (bug 976262) | ||
.on('input blur', _.throttle(function() {countChars(this, $cc);}, 250)) | ||
.trigger('blur'); | ||
@@ -183,2 +184,9 @@ }); | ||
var a = document.createElement('a'); | ||
function urlparse(url) { | ||
a.href = url; | ||
return a; | ||
} | ||
return { | ||
@@ -198,2 +206,3 @@ '_pd': _pd, | ||
'urlparams': urlparams, | ||
'urlparse': urlparse, | ||
'urlunparam': urlunparam, | ||
@@ -200,0 +209,0 @@ 'translate': translate |
define('views/debug', | ||
['cache', 'capabilities', 'log', 'notification', 'requests', 'settings', 'storage', 'utils', 'z'], | ||
function(cache, capabilities, log, notification, requests, settings, storage, utils, z) { | ||
['buckets', 'cache', 'capabilities', 'log', 'models', 'notification', 'requests', 'settings', 'storage', 'user', 'utils', 'z'], | ||
function(buckets, cache, capabilities, log, models, notification, requests, settings, storage, user, utils, z) { | ||
'use strict'; | ||
var persistent_console_debug = log.persistent('debug', 'change'); | ||
var persistent_console_network = log.persistent('mobilenetwork', 'change'); | ||
var label = $(document.getElementById('debug-status')); | ||
@@ -11,2 +14,22 @@ z.doc.on('click', '#clear-localstorage', function(e) { | ||
}).on('click', '#enable-offline-cache', function() { | ||
storage.removeItem('offline_cache_disabled'); | ||
persistent_console_debug.log('Offline cache enabled:', new Date()); | ||
require('views').reload(); | ||
notification.notification({message: 'Offline cache enabled', timeout: 1000}); | ||
}).on('click', '#disable-offline-cache', function() { | ||
storage.setItem('offline_cache_disabled', '1'); | ||
persistent_console_debug.log('Offline cache disabled:', new Date()); | ||
require('views').reload(); | ||
notification.notification({message: 'Offline cache disabled', timeout: 1000}); | ||
}).on('click', '#clear-offline-cache', function() { | ||
cache.flush(); | ||
// This actually flushes all model caches. | ||
models('app').flush(); | ||
persistent_console_debug.log('Offline cache cleared:', new Date()); | ||
notification.notification({message: 'Offline cache cleared', timeout: 1000}); | ||
window.location.reload(); | ||
}).on('click', '#clear-cookies', function() { | ||
@@ -21,2 +44,6 @@ var cookies = document.cookie.split(';'); | ||
}).on('click', '#nukecounter', function(e) { | ||
storage.removeItem('newscounter'); | ||
notification.notification({message: 'newscounter reset', timeout: 1000}); | ||
}).on('click', '.cache-menu a', function(e) { | ||
@@ -38,3 +65,4 @@ e.preventDefault(); | ||
settings: settings, | ||
report_version: 1.0 | ||
report_version: 1.0, | ||
profile: buckets.profile | ||
})}; | ||
@@ -47,2 +75,22 @@ requests.post('https://ashes.paas.allizom.org/post_report', data).done(function(data) { | ||
}); | ||
}).on('change', '#debug-page select[name=region]', function(e) { | ||
var val = $(this).val(); | ||
var current_region = user.get_setting('region_override'); | ||
if (current_region !== val) { | ||
persistent_console_network.log('Manual region override change:', current_region, '→', val); | ||
} | ||
user.update_settings({region_override: val}); | ||
z.page.trigger('reload_chrome'); | ||
notification.notification({message: 'Region updated to ' + (settings.REGION_CHOICES_SLUG[val] || '---')}); | ||
}).on('change', '#debug-page select[name=carrier]', function(e) { | ||
var val = $(this).val(); | ||
var current_carrier = user.get_setting('carrier_override'); | ||
if (current_carrier !== val) { | ||
persistent_console_network.log('Manual carrier override change:', current_carrier, '→', val); | ||
} | ||
user.update_settings({carrier_override: val}); | ||
z.page.trigger('reload_chrome'); | ||
notification.notification({message: 'Carrier updated to ' + val}); | ||
}); | ||
@@ -54,7 +102,10 @@ | ||
builder.start('debug.html', { | ||
carriers: require('mobilenetwork').carriers, | ||
cache: cache.raw, | ||
capabilities: capabilities, | ||
profile: buckets.profile, | ||
recent_logs: recent_logs, | ||
persistent_logs: log.persistent.all, | ||
filter: log.filter | ||
filter: log.filter, | ||
request_cache: JSON.parse(storage.getItem('request_cache') || '{}') | ||
}); | ||
@@ -61,0 +112,0 @@ |
@@ -7,2 +7,3 @@ (function() { | ||
var eeq_ = a.eeq_; | ||
var feq_ = a.feq_; | ||
var mock = a.mock; | ||
@@ -71,3 +72,7 @@ | ||
'cache', | ||
{}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; } | ||
} | ||
}, | ||
function(cache) { | ||
@@ -248,2 +253,106 @@ var key = 'test2:'; | ||
test('cache get_ttl', function(done) { | ||
mock( | ||
'cache', | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return true; }, | ||
offline_cache_whitelist: { | ||
'/api/v1/fireplace/consumer-info/': 60 * 60, // 1 hour in seconds | ||
'/api/v1/fireplace/search/featured/': 60 * 60 * 6, // 6 hours | ||
'/api/v1/apps/category/': 60 * 60 * 24 // 1 day | ||
} | ||
} | ||
}, | ||
function (cache) { | ||
eq_(cache.get_ttl('https://omg.org/api/v1/fireplace/consumer-info/'), | ||
60 * 60 * 1000); // 1 hour in microseconds | ||
eq_(cache.get_ttl('https://omg.org/api/v1/apps/category/'), | ||
60 * 60 * 24 * 1000); // 1 hour in microseconds | ||
eq_(cache.get_ttl('https://omg.org/api/v1/swag/yolo/foreva/'), null); | ||
done(); | ||
} | ||
); | ||
}); | ||
test('cache flush_signed', function(done) { | ||
mock( | ||
'cache', | ||
{ | ||
user: { | ||
logged_in: function() { return true; }, | ||
get_setting: function(x) {}, | ||
get_token: function() { return 'SwaggasaurusRex';} | ||
} | ||
}, | ||
function (cache) { | ||
var data = 'ratchet data'; | ||
var signed_url = 'https://omg.org/api/v1/app/yolo/?_user=SwaggasaurusRex'; | ||
cache.set(signed_url, data); | ||
eq_(cache.get(signed_url), data); | ||
var unsigned_url = 'https://omg.org/api/v1/app/swag/'; | ||
cache.set(unsigned_url, data); | ||
eq_(cache.get(unsigned_url), data); | ||
feq_(Object.keys(cache.cache).sort(), [unsigned_url, signed_url]); | ||
// Calling this should clear all cache keys whose URLs contain | ||
// `_user=<token>`. | ||
cache.flush_signed(); | ||
feq_(Object.keys(cache.cache), [unsigned_url]); | ||
done(); | ||
} | ||
); | ||
}); | ||
test('cache flush_expired', function(done) { | ||
mock( | ||
'cache', | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return true; }, | ||
offline_cache_whitelist: { | ||
'/api/v1/fireplace/consumer-info/': 60 * 60, // 1 hour in seconds | ||
'/api/v1/fireplace/search/featured/': 60 * 60 * 6, // 6 hours | ||
'/api/v1/apps/category/': 60 * 60 * 24 // 1 day | ||
} | ||
} | ||
}, | ||
function (cache) { | ||
// Both were just added and unexpired ... | ||
cache.set('https://omg.org/api/v1/fireplace/consumer-info/', { | ||
'__time': +new Date() | ||
}); | ||
cache.set('https://omg.org/api/v1/fireplace/search/featured/', { | ||
'__time': +new Date() | ||
}); | ||
cache.flush_expired(); | ||
assert(cache.has('https://omg.org/api/v1/fireplace/consumer-info/')); | ||
assert(cache.has('https://omg.org/api/v1/fireplace/search/featured/')); | ||
// Neither has expired ... | ||
cache.set('https://omg.org/api/v1/fireplace/consumer-info/', { | ||
'__time': +new Date() - (60 * 59 * 1000) // 59 min ago in microseconds | ||
}); | ||
cache.flush_expired(); | ||
assert(cache.has('https://omg.org/api/v1/fireplace/consumer-info/')); | ||
assert(cache.has('https://omg.org/api/v1/fireplace/search/featured/')); | ||
// One has expired! | ||
cache.set('https://omg.org/api/v1/fireplace/consumer-info/', { | ||
'__time': +new Date() - (60 * 65 * 1000) // 1 hr 5 min ago in microseconds | ||
}); | ||
cache.flush_expired(); | ||
assert(!cache.has('https://omg.org/api/v1/fireplace/consumer-info/')); | ||
assert(cache.has('https://omg.org/api/v1/fireplace/search/featured/')); | ||
done(); | ||
} | ||
); | ||
}); | ||
})(); |
@@ -23,3 +23,8 @@ (function() { | ||
'models', | ||
{settings: {model_prototypes: {'dummy': 'id', 'dummy2': 'id'}}}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id', 'dummy2': 'id'} | ||
} | ||
}, | ||
function(models) { | ||
@@ -61,3 +66,8 @@ var d1 = models('dummy'); | ||
'models', | ||
{settings: {model_prototypes: {'dummy': 'id'}}}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
function(models) { | ||
@@ -84,3 +94,8 @@ var d1 = models('dummy'); | ||
'models', | ||
{settings: {model_prototypes: {'dummy': 'id'}}}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
function(models) { | ||
@@ -107,3 +122,8 @@ var d1 = models('dummy'); | ||
'models', | ||
{settings: {model_prototypes: {'dummy': 'id'}}}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
function(models) { | ||
@@ -132,3 +152,8 @@ var d1 = models('dummy'); | ||
'models', | ||
{settings: {model_prototypes: {'dummy': 'id'}}}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
function(models) { | ||
@@ -173,3 +198,6 @@ var d1 = models('dummy'); | ||
requests: {get: function(x) {return 'surprise! ' + x;}}, | ||
settings: {model_prototypes: {'dummy': 'id'}} | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
@@ -199,3 +227,6 @@ function(models) { | ||
requests: {get: function(x) {return 'surprise! ' + x;}}, | ||
settings: {model_prototypes: {'dummy': 'id'}} | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
@@ -217,3 +248,6 @@ function(models) { | ||
requests: {get: function(x) {return "not the droids you're looking for";}}, | ||
settings: {model_prototypes: {'dummy': 'id'}} | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
@@ -235,3 +269,8 @@ function(models) { | ||
'models', | ||
{settings: {model_prototypes: {'dummy': 'id'}}}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
function(models) { | ||
@@ -262,3 +301,8 @@ var d1 = models('dummy'); | ||
'models', | ||
{settings: {model_prototypes: {'dummy': 'id'}}}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
function(models) { | ||
@@ -283,3 +327,8 @@ var d1 = models('dummy'); | ||
'models', | ||
{settings: {model_prototypes: {'dummy': 'id'}}}, | ||
{ | ||
settings: { | ||
offline_cache_enabled: function () { return false; }, | ||
model_prototypes: {'dummy': 'id'} | ||
} | ||
}, | ||
function(models) { | ||
@@ -286,0 +335,0 @@ var d1 = models('dummy'); |
@@ -80,8 +80,9 @@ (function() { | ||
routes_api: {'homepage': '/foo/homepage'}, | ||
routes_api_args: function() {return function() {return function() {return {foo: 'bar'};};};}, // Functions get pre-evaluated. | ||
settings: {api_url: 'api:'} | ||
settings: { | ||
api_url: 'api:', | ||
api_cdn_whitelist: {}, | ||
} | ||
}, function(urls) { | ||
var homepage_url = urls.api.url('homepage'); | ||
eq_(homepage_url.substr(0, 17), 'api:/foo/homepage'); | ||
contains(homepage_url, 'foo=bar'); | ||
done(); | ||
@@ -99,4 +100,6 @@ }, | ||
routes_api: {'homepage': '/foo/homepage'}, | ||
routes_api_args: function() {return function() {return function() {return {foo: 'bar'};};};}, // Functions get pre-evaluated. | ||
settings: {api_url: 'api:'}, | ||
settings: { | ||
api_url: 'api:', | ||
api_cdn_whitelist: {} | ||
}, | ||
user: { | ||
@@ -132,3 +135,7 @@ logged_in: function() { return true; }, | ||
routes_api: {'homepage': '/foo/homepage'}, | ||
settings: {api_url: 'api:', api_param_blacklist: ['region']} | ||
settings: { | ||
api_cdn_whitelist: {}, | ||
api_url: 'api:', | ||
api_param_blacklist: ['region'] | ||
} | ||
}, function(urls) { | ||
@@ -144,2 +151,32 @@ var homepage_url = urls.api.url('homepage'); | ||
test('api url CDN whitelist', function(done, fail) { | ||
mock( | ||
'urls', | ||
{ | ||
routes_api: { | ||
'homepage': '/api/v1/homepage/', | ||
'search': '/api/v1/fireplace/search/?swag=yolo' | ||
}, | ||
settings: { | ||
api_url: 'api:', | ||
api_cdn_whitelist: { | ||
'/api/v1/fireplace/search/': 60, // 1 minute | ||
'/api/v1/fireplace/search/featured/': 60 * 2, // 2 minutes | ||
}, | ||
media_url: 'http://cdn.so.fast.omg.org' | ||
} | ||
}, function(urls) { | ||
var homepage_url = urls.api.url('homepage'); | ||
eq_(homepage_url.substr(0, 21), 'api:/api/v1/homepage/'); | ||
var search_url = urls.api.url('search'); | ||
eq_(search_url.substr(0, 51), | ||
'http://cdn.so.fast.omg.org/api/v1/fireplace/search/'); | ||
done(); | ||
}, | ||
fail | ||
); | ||
}); | ||
test('api url params', function(done, fail) { | ||
@@ -150,3 +187,6 @@ mock( | ||
routes_api: {'homepage': '/foo/asdf'}, | ||
settings: {api_url: 'api:'} | ||
settings: { | ||
api_url: 'api:', | ||
api_cdn_whitelist: {} | ||
} | ||
}, function(urls) { | ||
@@ -153,0 +193,0 @@ var homepage_url = urls.api.params('homepage', {q: 'poop'}); |
@@ -138,5 +138,2 @@ (function() { | ||
eq_(filters.translate({}, dlobj, 'es-PD'), ''); | ||
eq_(filters.translate('', dlobj, 'es-PD'), ''); | ||
eq_(filters.translate(null, dlobj, 'es-PD'), ''); | ||
eq_(filters.translate(undefined, dlobj, 'es-PD'), ''); | ||
done(); | ||
@@ -143,0 +140,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
905811
84
12519