Created
May 22, 2015 02:01
-
-
Save robwormald/4facf5fe984299def26e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
var HOUR = 1000*60*60; | |
/** | |
* Battery logger calculates battery discharge rate at the most opportune moment. | |
*/ | |
function BatteryLogger() { | |
// Instantaneous values: is it charging, and the current level. | |
this.isCharging = true; | |
this.batteryLevel = -1; | |
// Snapshot variables. | |
this.snapshotLevel = -1; | |
this.snapshotTime = 0; | |
this.init(); | |
} | |
BatteryLogger.prototype.init = function() { | |
console.log('Started logging battery level'); | |
// Watch the battery charging state. | |
navigator.getBattery().then(function(battery) { | |
this.updateBatteryStatus_(battery); | |
battery.onchargingchange = function() { | |
this.updateBatteryStatus_(battery); | |
}.bind(this); | |
battery.onlevelchange = function() { | |
this.updateBatteryStatus_(battery); | |
}.bind(this); | |
}.bind(this)); | |
}; | |
/** | |
* Whether or not enough time has passed since the snapshot so that | |
* the battery discharge rate should be reported. | |
*/ | |
BatteryLogger.prototype.isReadyToReport = function() { | |
var elapsed = new Date() - this.snapshotTime; | |
return !this.isCharging && elapsed > HOUR/6; | |
}; | |
/** | |
* Gets the discharge rate calculated since the last snapshot. This | |
* should be an estimation of how many percentage points the battery drains | |
* every hour. | |
*/ | |
BatteryLogger.prototype.getDischargeRate = function() { | |
if (!this.isReadyToReport()) { | |
console.error('Not ready to report discharge rate yet!'); | |
return null; | |
} | |
var time = new Date() - this.snapshotTime; | |
var delta = this.snapshotLevel - this.batteryLevel; | |
var percent = delta/time * HOUR; | |
/// Return an integer in [0, 100]. | |
return Math.floor(percent * 100); | |
}; | |
BatteryLogger.prototype.updateBatteryStatus_ = function(battery) { | |
var didStartDischarging = !battery.charging && this.isCharging; | |
var didStopDischarging = battery.charging && !this.isCharging; | |
// Store some important variables. | |
this.isCharging = battery.charging; | |
this.batteryLevel = battery.level; | |
// If the battery starts discharging, setup an hourly timer. | |
if (didStartDischarging) { | |
console.log('Unplugged: battery discharging.'); | |
this.snapshot(); | |
} | |
// If the battery stops discharging, stop the timer. | |
if (didStopDischarging) { | |
console.log('Plugged in: battery charging.'); | |
} | |
}; | |
/** | |
* Runs a snapshot of the current battery state. In particular, capture the | |
* current time and the current battery level. | |
*/ | |
BatteryLogger.prototype.snapshot = function() { | |
console.log('Snapshotting battery state'); | |
this.snapshotTime = new Date(); | |
this.snapshotLevel = this.batteryLevel; | |
}; | |
module.exports = BatteryLogger; | |
},{}],2:[function(require,module,exports){ | |
var MessageFilter = require('./message-filter.js'); | |
// Hard-coded for M41, based on WhisperNet parameters: | |
// https://sites.google.com/a/google.com/audio-whispernet/dtmf/standard-parameters | |
// The token is encoded in 14 symbols, each 125ms long. | |
var AUDIBLE_DURATION = 1750; // 14 * 125. | |
var INAUDIBLE_DURATION = 5000; | |
var RESUBSCRIBE_DURATION = 1000 * 60 * 10; // 10 minutes. | |
// TODO(smus): Try to reduce this as the copresence API evolves. | |
var VERIFY_DURATION = 4500; | |
var AUDIBLE_ID = 'shoutout_audible'; | |
var INAUDIBLE_ID = 'shoutout_inaudible'; | |
var SUBSCRIBE_ID = 'subscribe_shoutout'; | |
var MESSAGE_TYPE = 'shoutout'; | |
// Events which CopresenceManager exposes. | |
var VALID_EVENTS = ['sent', 'received', 'unsubscribed', 'error']; | |
var PUBLISH_TEMPLATE = { | |
publish: { | |
id: AUDIBLE_ID, | |
message: { | |
type: MESSAGE_TYPE, | |
payload: null // Replace with data. | |
}, | |
timeToLiveMillis: AUDIBLE_DURATION, | |
strategies: { | |
onlyBroadcast: true, | |
audible: true // Replace with whether or not it's audible. | |
} | |
} | |
}; | |
var UNPUBLISH_TEMPLATE = { | |
unpublish: { | |
unpublishId: AUDIBLE_ID | |
} | |
}; | |
var SUBSCRIBE_TEMPLATE = { | |
subscribe: { | |
id: SUBSCRIBE_ID, | |
filter: { | |
type: MESSAGE_TYPE | |
}, | |
timeToLiveMillis: 86400000, // 1 day | |
strategies: { | |
onlyScan: true, | |
audible: true | |
} | |
} | |
}; | |
var UNSUBSCRIBE_TEMPLATE = { | |
unsubscribe: { | |
unsubscribeId: SUBSCRIBE_ID | |
} | |
}; | |
var messageFilter = new MessageFilter(); | |
function CopresenceManager() { | |
this.callbacks = {}; | |
// Save the last sent URL. | |
this.lastUrl = null; | |
// Track whether or not the last publish was verified. | |
this.isLastPublishVerified = true; | |
// Initialize the copresence stack. | |
this.initialize(); | |
} | |
CopresenceManager.Error = { | |
PUBLISH_FAILED: 1 | |
}; | |
CopresenceManager.prototype.initialize = function() { | |
// API key from console.developers.google.com. | |
chrome.copresence.setApiKey('AIzaSyBsF7ryM_uG_qv3DtjPgDL9aeZvCsJ8hS4'); | |
chrome.copresence.onStatusUpdated.addListener(this.onStatusUpdated_.bind(this)); | |
chrome.copresence.onMessagesReceived.addListener(this.onMessagesReceived_.bind(this)); | |
} | |
CopresenceManager.prototype.subscribe = function() { | |
if (this.isListening_) { | |
console.error('Cannot start listening: already listening.'); | |
return; | |
} | |
chrome.copresence.execute([SUBSCRIBE_TEMPLATE], this.onSubscribe_.bind(this)); | |
// Resubscribe every 10 minutes. | |
this.resubscribeTimer_ = setInterval(this.resubscribe_.bind(this), RESUBSCRIBE_DURATION); | |
}; | |
CopresenceManager.prototype.unsubscribe = function() { | |
if (!this.isListening_) { | |
console.error('Cannot stop listening: not listening yet.'); | |
return; | |
} | |
chrome.copresence.execute([UNSUBSCRIBE_TEMPLATE], this.onUnsubscribe_.bind(this)); | |
// Cancel the resubscribe interval timer. | |
clearInterval(this.resubscribeTimer_); | |
}; | |
CopresenceManager.prototype.send = function(tab, userInfo) { | |
this.isLastPublishVerified = true; | |
var data = { | |
url: tab.url, | |
name: userInfo.name, | |
picture: userInfo.picture, | |
}; | |
if (tab.title) { | |
data.title = tab.title; | |
} | |
var audible = this.getPublishTemplate_({ | |
payload: data, | |
audible: true, | |
timeToLiveMillis: AUDIBLE_DURATION | |
}); | |
var inaudible = this.getPublishTemplate_({ | |
payload: data, | |
audible: false, | |
timeToLiveMillis: INAUDIBLE_DURATION | |
}); | |
chrome.copresence.execute([inaudible, audible], this.onPublish_.bind(this)); | |
// Save the last sent URL. | |
this.lastUrl = tab.url; | |
}; | |
CopresenceManager.prototype.on = function(eventName, callback) { | |
if (VALID_EVENTS.indexOf(eventName) < 0) { | |
console.error('Invalid event!'); | |
return; | |
} | |
this.callbacks[eventName] = callback; | |
}; | |
CopresenceManager.prototype.removeListener = function(eventName) { | |
delete this.callbacks[eventName] | |
}; | |
CopresenceManager.prototype.getLastUrl = function() { | |
return this.lastUrl; | |
}; | |
CopresenceManager.prototype.isListening = function() { | |
return this.isListening_; | |
}; | |
/*** PRIVATE ***/ | |
CopresenceManager.prototype.onStatusUpdated_ = function(status) { | |
console.log('onStatusUpdated_', status); | |
this.isLastPublishVerified = false; | |
}; | |
CopresenceManager.prototype.onMessagesReceived_ = function(subscriptionId, messages) { | |
log('got messages: ' + messages + ' from ' + subscriptionId); | |
for (var i = 0; i < messages.length; i++) { | |
var message = messages[i]; | |
var textDecoder = new TextDecoder(); | |
var jsonString = textDecoder.decode(message.payload); | |
// TODO(smus): Investigate why some pages (eg. | |
// https://www.google.com/intl/en/about/company/facts/management/) have | |
// a C-S in the JSON (instead of an em-dash). | |
var data = JSON.parse(jsonString); | |
if (messageFilter.isNew(data)) { | |
this.fire_(this.callbacks.received, data); | |
} | |
} | |
}; | |
CopresenceManager.prototype.onPublish_ = function(status) { | |
log('onPublish_', status); | |
if (status == 'success') { | |
// Upon publish success, wait some time before deciding if we failed or | |
// if we succeeded. | |
clearTimeout(this.verifyTimer); | |
this.verifyTimer = setTimeout(this.onVerifyPublish_.bind(this), | |
VERIFY_DURATION); | |
} | |
if (status == 'failed') { | |
this.fire_(this.callbacks.error, CopresenceManager.Error.PUBLISH_FAILED); | |
} | |
}; | |
CopresenceManager.prototype.onVerifyPublish_ = function() { | |
this.fire_(this.callbacks.sent, this.isLastPublishVerified); | |
}; | |
CopresenceManager.prototype.onSubscribe_ = function(status) { | |
log('onSubscribe_', status); | |
this.isListening_ = true; | |
}; | |
CopresenceManager.prototype.onUnpublish_ = function(status) { | |
log('onUnpublish_', status); | |
}; | |
CopresenceManager.prototype.onUnsubscribe_ = function(status) { | |
log('onUnsubscribe_', status); | |
this.isListening_ = false; | |
this.fire_(this.callbacks.unsubscribed); | |
}; | |
CopresenceManager.prototype.fire_ = function(callback) { | |
if (!callback) { | |
console.log('No valid callback specified.'); | |
return; | |
} | |
var args = [].slice.call(arguments) | |
// Eliminate the first param (the callback). | |
args.shift(); | |
callback.apply(this, args); | |
}; | |
CopresenceManager.prototype.resubscribe_ = function() { | |
this.unsubscribe(); | |
this.on('unsubscribed', function() { | |
this.subscribe(); | |
this.removeListener('unsubscribed'); | |
}.bind(this)); | |
}; | |
CopresenceManager.prototype.getPublishTemplate_ = function(params) { | |
// Params are {audible: boolean, payload: JSON string, timeToLiveMillis: int}. | |
params = params || {}; | |
var payload = params.payload; | |
var audible = !!params.audible; | |
var timeToLiveMillis = params.timeToLiveMillis; | |
if (payload == null) { | |
return console.error('No payload specified!'); | |
} | |
var textEncoder = new TextEncoder(); | |
var obj = JSON.parse(JSON.stringify(PUBLISH_TEMPLATE)) | |
obj.publish.message.payload = textEncoder.encode(JSON.stringify(payload)).buffer; | |
obj.publish.strategies.audible = audible; | |
if (timeToLiveMillis) { | |
obj.publish.timeToLiveMillis = timeToLiveMillis; | |
} | |
obj.publish.id = this.getPublishId_(audible); | |
return obj; | |
}; | |
CopresenceManager.prototype.getUnpublishTemplate_ = function(params) { | |
// Params are {audible: boolean}. | |
params = params || {}; | |
var audible = !!params.audible; | |
var obj = JSON.parse(JSON.stringify(UNPUBLISH_TEMPLATE)) | |
obj.unpublish.unpublishId = this.getPublishId_(audible); | |
return obj; | |
}; | |
CopresenceManager.prototype.getPublishId_ = function(audible) { | |
return audible ? AUDIBLE_ID : INAUDIBLE_ID; | |
}; | |
function log() { | |
// Handle variable args. | |
var args = [].slice.call(arguments) | |
args.unshift('[CM]'); | |
var now = new Date(); | |
ts = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds(); | |
args.unshift(ts); | |
console.log.apply(console, args); | |
}; | |
module.exports = CopresenceManager; | |
},{"./message-filter.js":7}],3:[function(require,module,exports){ | |
(function() { var g,aa=aa||{},h=this,ba=function(){},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&& | |
!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},m=function(a){return"array"==ca(a)},da=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},n=function(a){return"string"==typeof a},ea=function(a){return"number"==typeof a},p=function(a){return"function"==ca(a)},q=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},fa=function(a,b,c){return a.call.apply(a.bind, | |
arguments)},ga=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}},r=function(a,b,c){r=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?fa:ga;return r.apply(null,arguments)},ha=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b= | |
c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},s=Date.now||function(){return+new Date},t=function(a,b){var c=a.split("."),d=h;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},u=function(a,b){function c(){}c.prototype=b.prototype;a.L=b.prototype;a.prototype=new c;a.Pc=function(a,c,f){return b.prototype[c].apply(a,Array.prototype.slice.call(arguments,2))}}; | |
Function.prototype.bind=Function.prototype.bind||function(a,b){if(1<arguments.length){var c=Array.prototype.slice.call(arguments,1);c.unshift(this,a);return r.apply(null,c)}return r(this,a)};var v=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,v);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};u(v,Error);v.prototype.name="CustomError";var ia=function(a,b){return a<b?-1:a>b?1:0};var w=Array.prototype,ja=w.indexOf?function(a,b,c){return w.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(n(a))return n(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},ka=w.forEach?function(a,b,c){w.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=n(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},la=w.some?function(a,b,c){return w.some.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=n(a)?a.split(""): | |
a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return!0;return!1},ma=w.every?function(a,b,c){return w.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=n(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return!1;return!0},oa=function(a){var b;t:{b=na;for(var c=a.length,d=n(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break t}b=-1}return 0>b?null:n(a)?a.charAt(b):a[b]},pa=function(a,b){var c=ja(a,b),d;(d=0<=c)&&w.splice.call(a,c,1);return d},qa=function(a){return w.concat.apply(w, | |
arguments)},ra=function(a,b,c){return 2>=arguments.length?w.slice.call(a,b):w.slice.call(a,b,c)};var sa="StopIteration"in h?h.StopIteration:Error("StopIteration"),ta=function(){};ta.prototype.next=function(){throw sa;};ta.prototype.vc=function(){return this};var ua=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},va=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b},wa=function(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b},xa=function(a,b){var c;t:{for(c in a)if(b.call(void 0,a[c],c,a))break t;c=void 0}return c&&a[c]},ya="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),za=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<ya.length;f++)c= | |
ya[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var x=function(a,b){this.p={};this.b=[];this.oa=this.h=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.ia(a)};x.prototype.t=function(){Aa(this);for(var a=[],b=0;b<this.b.length;b++)a.push(this.p[this.b[b]]);return a};x.prototype.F=function(){Aa(this);return this.b.concat()};x.prototype.Q=function(a){return y(this.p,a)}; | |
x.prototype.remove=function(a){return y(this.p,a)?(delete this.p[a],this.h--,this.oa++,this.b.length>2*this.h&&Aa(this),!0):!1};var Aa=function(a){if(a.h!=a.b.length){for(var b=0,c=0;b<a.b.length;){var d=a.b[b];y(a.p,d)&&(a.b[c++]=d);b++}a.b.length=c}if(a.h!=a.b.length){for(var e={},c=b=0;b<a.b.length;)d=a.b[b],y(e,d)||(a.b[c++]=d,e[d]=1),b++;a.b.length=c}};g=x.prototype;g.get=function(a,b){return y(this.p,a)?this.p[a]:b}; | |
g.set=function(a,b){y(this.p,a)||(this.h++,this.b.push(a),this.oa++);this.p[a]=b};g.ia=function(a){var b;a instanceof x?(b=a.F(),a=a.t()):(b=wa(a),a=va(a));for(var c=0;c<b.length;c++)this.set(b[c],a[c])};g.forEach=function(a,b){for(var c=this.F(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};g.clone=function(){return new x(this)};g.Jb=function(){Aa(this);for(var a={},b=0;b<this.b.length;b++){var c=this.b[b];a[c]=this.p[c]}return a}; | |
g.vc=function(a){Aa(this);var b=0,c=this.b,d=this.p,e=this.oa,f=this,k=new ta;k.next=function(){for(;;){if(e!=f.oa)throw Error("The map has changed since the iterator was created");if(b>=c.length)throw sa;var k=c[b++];return a?k:d[k]}};return k};var y=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Ba,Ca,Da={id:"hitType",name:"t",valueType:"text",maxLength:void 0,defaultValue:void 0},Ea={id:"sessionControl",name:"sc",valueType:"text",maxLength:void 0,defaultValue:void 0},Fa={id:"description",name:"cd",valueType:"text",maxLength:2048,defaultValue:void 0},Ga={id:"eventCategory",name:"ec",valueType:"text",maxLength:150,defaultValue:void 0},Ha={id:"eventAction",name:"ea",valueType:"text",maxLength:500,defaultValue:void 0},Ia={id:"eventLabel",name:"el",valueType:"text",maxLength:500,defaultValue:void 0}, | |
Ja={id:"eventValue",name:"ev",valueType:"integer",maxLength:void 0,defaultValue:void 0},Ka={pd:Da,Qc:{id:"anonymizeIp",name:"aip",valueType:"boolean",maxLength:void 0,defaultValue:void 0},Ad:{id:"queueTime",name:"qt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Wc:{id:"cacheBuster",name:"z",valueType:"text",maxLength:void 0,defaultValue:void 0},Gd:Ea,Wd:{id:"userId",name:"uid",valueType:"text",maxLength:void 0,defaultValue:void 0},xd:{id:"nonInteraction",name:"ni",valueType:"boolean", | |
maxLength:void 0,defaultValue:void 0},fd:Fa,Pd:{id:"title",name:"dt",valueType:"text",maxLength:1500,defaultValue:void 0},Sc:{id:"appId",name:"aid",valueType:"text",maxLength:150,defaultValue:void 0},Tc:{id:"appInstallerId",name:"aiid",valueType:"text",maxLength:150,defaultValue:void 0},jd:Ga,hd:Ha,kd:Ia,ld:Ja,Id:{id:"socialNetwork",name:"sn",valueType:"text",maxLength:50,defaultValue:void 0},Hd:{id:"socialAction",name:"sa",valueType:"text",maxLength:50,defaultValue:void 0},Jd:{id:"socialTarget", | |
name:"st",valueType:"text",maxLength:2048,defaultValue:void 0},Sd:{id:"transactionId",name:"ti",valueType:"text",maxLength:500,defaultValue:void 0},Rd:{id:"transactionAffiliation",name:"ta",valueType:"text",maxLength:500,defaultValue:void 0},Td:{id:"transactionRevenue",name:"tr",valueType:"currency",maxLength:void 0,defaultValue:void 0},Ud:{id:"transactionShipping",name:"ts",valueType:"currency",maxLength:void 0,defaultValue:void 0},Vd:{id:"transactionTax",name:"tt",valueType:"currency",maxLength:void 0, | |
defaultValue:void 0},dd:{id:"currencyCode",name:"cu",valueType:"text",maxLength:10,defaultValue:void 0},td:{id:"itemPrice",name:"ip",valueType:"currency",maxLength:void 0,defaultValue:void 0},ud:{id:"itemQuantity",name:"iq",valueType:"integer",maxLength:void 0,defaultValue:void 0},rd:{id:"itemCode",name:"ic",valueType:"text",maxLength:500,defaultValue:void 0},sd:{id:"itemName",name:"in",valueType:"text",maxLength:500,defaultValue:void 0},qd:{id:"itemCategory",name:"iv",valueType:"text",maxLength:500, | |
defaultValue:void 0},bd:{id:"campaignSource",name:"cs",valueType:"text",maxLength:100,defaultValue:void 0},$c:{id:"campaignMedium",name:"cm",valueType:"text",maxLength:50,defaultValue:void 0},ad:{id:"campaignName",name:"cn",valueType:"text",maxLength:100,defaultValue:void 0},Zc:{id:"campaignKeyword",name:"ck",valueType:"text",maxLength:500,defaultValue:void 0},Xc:{id:"campaignContent",name:"cc",valueType:"text",maxLength:500,defaultValue:void 0},Yc:{id:"campaignId",name:"ci",valueType:"text",maxLength:100, | |
defaultValue:void 0},od:{id:"gclid",name:"gclid",valueType:"text",maxLength:void 0,defaultValue:void 0},ed:{id:"dclid",name:"dclid",valueType:"text",maxLength:void 0,defaultValue:void 0},zd:{id:"pageLoadTime",name:"plt",valueType:"integer",maxLength:void 0,defaultValue:void 0},gd:{id:"dnsTime",name:"dns",valueType:"integer",maxLength:void 0,defaultValue:void 0},Kd:{id:"tcpConnectTime",name:"tcp",valueType:"integer",maxLength:void 0,defaultValue:void 0},Fd:{id:"serverResponseTime",name:"srt",valueType:"integer", | |
maxLength:void 0,defaultValue:void 0},yd:{id:"pageDownloadTime",name:"pdt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Bd:{id:"redirectResponseTime",name:"rrt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Ld:{id:"timingCategory",name:"utc",valueType:"text",maxLength:150,defaultValue:void 0},Od:{id:"timingVar",name:"utv",valueType:"text",maxLength:500,defaultValue:void 0},Nd:{id:"timingValue",name:"utt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Md:{id:"timingLabel", | |
name:"utl",valueType:"text",maxLength:500,defaultValue:void 0},md:{id:"exDescription",name:"exd",valueType:"text",maxLength:150,defaultValue:void 0},nd:{id:"exFatal",name:"exf",valueType:"boolean",maxLength:void 0,defaultValue:"1"}},La=function(a){if(1>a||200<a)throw Error("Expected dimension index range 1-200, but was : "+a);return{id:"dimension"+a,name:"cd"+a,valueType:"text",maxLength:150,defaultValue:void 0}},Ma=function(a){if(1>a||200<a)throw Error("Expected metric index range 1-200, but was : "+ | |
a);return{id:"metric"+a,name:"cm"+a,valueType:"integer",maxLength:void 0,defaultValue:void 0}};var Na=function(a){if(1>a)return"0";if(3>a)return"1-2";a=Math.floor(Math.log(a-1)/Math.log(2));return Math.pow(2,a)+1+"-"+Math.pow(2,a+1)},Oa=function(a,b){for(var c=0,d=a.length-1,e=0;c<=d;){var f=Math.floor((c+d)/2),e=a[f];if(b<=e){d=0==f?0:a[f-1];if(b>d)return(d+1).toString()+"-"+e.toString();d=f-1}else if(b>e){if(f>=a.length-1)return(a[a.length-1]+1).toString()+"+";c=f+1}}return"<= 0"};var z=function(){this.ab=[]},Pa=function(){return new z};g=z.prototype;g.when=function(a){this.ab.push(a);return this};g.zb=function(a){var b=arguments;this.when(function(a){return 0<=ja(b,a.Gb())});return this};g.Oc=function(a,b){var c=ra(arguments,1);this.when(function(b){b=b.T().get(a);return 0<=ja(c,b)});return this};g.xb=function(a,b){if(q(this.e))throw Error("Filter has already been set.");this.e=q(b)?r(a,b):a;return this}; | |
g.Ca=function(){if(0==this.ab.length)throw Error("Must specify at least one predicate using #when or a helper method.");if(!q(this.e))throw Error("Must specify a delegate filter using #applyFilter.");return r(function(a){ma(this.ab,function(b){return b(a)})&&this.e(a)},this)};var A=function(){this.Ab=!1;this.Bb="";this.qb=!1;this.za=null};A.prototype.wc=function(a){this.Ab=!0;this.Bb=a||" - ";return this};A.prototype.Nc=function(){this.qb=!0;return this};A.prototype.Ec=function(){return Qa(this,Na)};A.prototype.Fc=function(a){return Qa(this,ha(Oa,a))}; | |
var Qa=function(a,b){if(null!=a.za)throw Error("LabelerBuilder: Only one labeling strategy may be used.");a.za=r(function(a){var d=a.T().get(Ja),e=a.T().get(Ia);ea(d)&&(d=b(d),null!=e&&this.Ab&&(d=e+this.Bb+d),a.T().set(Ia,d))},a);return a};A.prototype.Ca=function(){if(null==this.za)throw Error("LabelerBuilder: a labeling strategy must be specified prior to calling build().");return Pa().zb("event").xb(r(function(a){this.za(a);this.qb&&a.T().remove(Ja)},this)).Ca()};var Ra=function(a,b){var c=Array.prototype.slice.call(arguments),d=c.shift();if("undefined"==typeof d)throw Error("[goog.string.format] Template required");return d.replace(/%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g,function(a,b,d,l,N,J,U,V){if("%"==J)return"%";var Db=c.shift();if("undefined"==typeof Db)throw Error("[goog.string.format] Not enough arguments");arguments[0]=Db;return B[J].apply(null,arguments)})},B={s:function(a,b,c){return isNaN(c)||""==c||a.length>=c?a:a=-1<b.indexOf("-",0)?a+Array(c- | |
a.length+1).join(" "):Array(c-a.length+1).join(" ")+a},f:function(a,b,c,d,e){d=a.toString();isNaN(e)||""==e||(d=a.toFixed(e));var f;f=0>a?"-":0<=b.indexOf("+")?"+":0<=b.indexOf(" ")?" ":"";0<=a&&(d=f+d);if(isNaN(c)||d.length>=c)return d;d=isNaN(e)?Math.abs(a).toString():Math.abs(a).toFixed(e);a=c-d.length-f.length;return d=0<=b.indexOf("-",0)?f+d+Array(a+1).join(" "):f+Array(a+1).join(0<=b.indexOf("0",0)?"0":" ")+d},d:function(a,b,c,d,e,f,k,l){return B.f(parseInt(a,10),b,c,d,0,f,k,l)}};B.i=B.d; | |
B.u=B.d;var Sa=function(a){if("function"==typeof a.t)return a.t();if(n(a))return a.split("");if(da(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return va(a)},Ta=function(a,b){if("function"==typeof a.forEach)a.forEach(b,void 0);else if(da(a)||n(a))ka(a,b,void 0);else{var c;if("function"==typeof a.F)c=a.F();else if("function"!=typeof a.t)if(da(a)||n(a)){c=[];for(var d=a.length,e=0;e<d;e++)c.push(e)}else c=wa(a);else c=void 0;for(var d=Sa(a),e=d.length,f=0;f<e;f++)b.call(void 0,d[f],c&&c[f], | |
a)}};var C=function(a){this.B=new x;for(var b=arguments,c=0;c<b.length;c+=2)this.set(b[c],b[c+1])};C.prototype.set=function(a,b){this.B.set(a.name,{key:a,value:b})};C.prototype.remove=function(a){this.B.remove(a.name)};C.prototype.get=function(a){a=this.B.get(a.name,null);return null===a?null:a.value};C.prototype.ia=function(a){this.B.ia(a.B)};var Ua=function(a,b){ka(a.B.t(),function(a){b(a.key,a.value)})};C.prototype.Jb=function(){var a={};Ua(this,function(b,c){a[b.id]=c});return a}; | |
C.prototype.clone=function(){var a=new C;a.B=this.B.clone();return a};C.prototype.toString=function(){var a={};Ua(this,function(b,c){a[b.id]=c});return JSON.stringify(a)};var D=function(a){this.e=a};g=D.prototype;g.xc=function(a){var b=new D(r(this.P,this));b.I=Ga;b.N=a;return b};g.action=function(a){var b=new D(r(this.P,this));b.I=Ha;b.N=a;return b};g.label=function(a){var b=new D(r(this.P,this));b.I=Ia;b.N=a;return b};g.value=function(a){var b=new D(r(this.P,this));b.I=Ja;b.N=a;return b};g.yc=function(a,b){var c=new D(r(this.P,this));c.I=La(a);c.N=b;return c};g.Dc=function(a,b){var c=new D(r(this.P,this));c.I=Ma(a);c.N=b;return c}; | |
g.send=function(a){var b=new C;this.P(b);return a.send("event",b)};g.P=function(a){null!=this.I&&null!=this.N&&!a.B.Q(this.I.name)&&a.set(this.I,this.N);q(this.e)&&this.e(a)};var Va=new D(ba);var E=function(){this.Y=this.Y;this.Da=this.Da};E.prototype.Y=!1;E.prototype.xa=function(){this.Y||(this.Y=!0,this.l())};E.prototype.l=function(){if(this.Da)for(;this.Da.length;)this.Da.shift()()};var F=function(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.U=!1;this.kb=!0};F.prototype.l=function(){};F.prototype.xa=function(){};F.prototype.preventDefault=function(){this.defaultPrevented=!0;this.kb=!1};var Wa=function(a){Wa[" "](a);return a};Wa[" "]=ba;var G;t:{var Xa=h.navigator;if(Xa){var Ya=Xa.userAgent;if(Ya){G=Ya;break t}}G=""}var H=function(a){return-1!=G.indexOf(a)};var Za=H("Opera")||H("OPR"),I=H("Trident")||H("MSIE"),K=H("Gecko")&&-1==G.toLowerCase().indexOf("webkit")&&!(H("Trident")||H("MSIE")),L=-1!=G.toLowerCase().indexOf("webkit"),$a=function(){var a=h.document;return a?a.documentMode:void 0},ab=function(){var a="",b;if(Za&&h.opera)return a=h.opera.version,p(a)?a():a;K?b=/rv\:([^\);]+)(\)|;)/:I?b=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:L&&(b=/WebKit\/(\S+)/);b&&(a=(a=b.exec(G))?a[1]:"");return I&&(b=$a(),b>parseFloat(a))?String(b):a}(),bb={},M=function(a){var b; | |
if(!(b=bb[a])){b=0;for(var c=String(ab).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),d=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var k=c[f]||"",l=d[f]||"",N=RegExp("(\\d*)(\\D*)","g"),J=RegExp("(\\d*)(\\D*)","g");do{var U=N.exec(k)||["","",""],V=J.exec(l)||["","",""];if(0==U[0].length&&0==V[0].length)break;b=ia(0==U[1].length?0:parseInt(U[1],10),0==V[1].length?0:parseInt(V[1],10))||ia(0==U[2].length,0==V[2].length)||ia(U[2],V[2])}while(0== | |
b)}b=bb[a]=0<=b}return b},cb=h.document,db=cb&&I?$a()||("CSS1Compat"==cb.compatMode?parseInt(ab,10):5):void 0;var eb=!I||I&&9<=db,fb=I&&!M("9"),gb=!L||M("528"),hb=K&&M("1.9b")||I&&M("8")||Za&&M("9.5")||L&&M("528"),ib=K&&!M("8")||I&&!M("9");var O=function(a,b){F.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.Db=this.state=null;if(a){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(K){var e;t:{try{Wa(d.nodeName);e=!0;break t}catch(f){}e=!1}e||(d=null)}}else"mouseover"== | |
c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=L||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=L||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey= | |
a.metaKey;this.state=a.state;this.Db=a;a.defaultPrevented&&this.preventDefault()}};u(O,F);O.prototype.preventDefault=function(){O.L.preventDefault.call(this);var a=this.Db;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,fb)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};O.prototype.l=function(){};var jb="closure_listenable_"+(1E6*Math.random()|0),kb=function(a){return!(!a||!a[jb])},lb=0;var mb=function(a,b,c,d,e){this.O=a;this.proxy=null;this.src=b;this.type=c;this.pa=!!d;this.sa=e;this.key=++lb;this.removed=this.qa=!1},nb=function(a){a.removed=!0;a.O=null;a.proxy=null;a.src=null;a.sa=null};var P=function(a){this.src=a;this.j={};this.Z=0};P.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.j[f];a||(a=this.j[f]=[],this.Z++);var k=ob(a,b,d,e);-1<k?(b=a[k],c||(b.qa=!1)):(b=new mb(b,this.src,f,!!d,e),b.qa=c,a.push(b));return b};P.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.j))return!1;var e=this.j[a];b=ob(e,b,c,d);return-1<b?(nb(e[b]),w.splice.call(e,b,1),0==e.length&&(delete this.j[a],this.Z--),!0):!1}; | |
var pb=function(a,b){var c=b.type;if(!(c in a.j))return!1;var d=pa(a.j[c],b);d&&(nb(b),0==a.j[c].length&&(delete a.j[c],a.Z--));return d};P.prototype.removeAll=function(a){a=a&&a.toString();var b=0,c;for(c in this.j)if(!a||c==a){for(var d=this.j[c],e=0;e<d.length;e++)++b,nb(d[e]);delete this.j[c];this.Z--}return b};P.prototype.X=function(a,b,c,d){a=this.j[a.toString()];var e=-1;a&&(e=ob(a,b,c,d));return-1<e?a[e]:null}; | |
var ob=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.removed&&f.O==b&&f.pa==!!c&&f.sa==d)return e}return-1};var qb="closure_lm_"+(1E6*Math.random()|0),rb={},sb=0,tb=function(a,b,c,d,e){if(m(b)){for(var f=0;f<b.length;f++)tb(a,b[f],c,d,e);return null}c=ub(c);return kb(a)?a.listen(b,c,d,e):vb(a,b,c,!1,d,e)},vb=function(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var k=!!e,l=wb(a);l||(a[qb]=l=new P(a));c=l.add(b,c,d,e,f);if(c.proxy)return c;d=xb();c.proxy=d;d.src=a;d.O=c;a.addEventListener?a.addEventListener(b.toString(),d,k):a.attachEvent(yb(b.toString()),d);sb++;return c},xb=function(){var a=zb, | |
b=eb?function(c){return a.call(b.src,b.O,c)}:function(c){c=a.call(b.src,b.O,c);if(!c)return c};return b},Ab=function(a,b,c,d,e){if(m(b)){for(var f=0;f<b.length;f++)Ab(a,b[f],c,d,e);return null}c=ub(c);return kb(a)?a.bb(b,c,d,e):vb(a,b,c,!0,d,e)},Bb=function(a,b,c,d,e){if(m(b))for(var f=0;f<b.length;f++)Bb(a,b[f],c,d,e);else c=ub(c),kb(a)?a.Va(b,c,d,e):a&&(a=wb(a))&&(b=a.X(b,c,!!d,e))&&Cb(b)},Cb=function(a){if(ea(a)||!a||a.removed)return!1;var b=a.src;if(kb(b))return pb(b.A,a);var c=a.type,d=a.proxy; | |
b.removeEventListener?b.removeEventListener(c,d,a.pa):b.detachEvent&&b.detachEvent(yb(c),d);sb--;(c=wb(b))?(pb(c,a),0==c.Z&&(c.src=null,b[qb]=null)):nb(a);return!0},yb=function(a){return a in rb?rb[a]:rb[a]="on"+a},Fb=function(a,b,c,d){var e=1;if(a=wb(a))if(b=a.j[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.pa==c&&!f.removed&&(e&=!1!==Eb(f,d))}return Boolean(e)},Eb=function(a,b){var c=a.O,d=a.sa||a.src;a.qa&&Cb(a);return c.call(d,b)},zb=function(a,b){if(a.removed)return!0;if(!eb){var c; | |
if(!(c=b))t:{c=["window","event"];for(var d=h,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break t}c=d}e=c;c=new O(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){t:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break t}catch(k){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,l=e.length-1;!c.U&&0<=l;l--)c.currentTarget=e[l],d&=Fb(e[l],f,!0,c);for(l=0;!c.U&&l<e.length;l++)c.currentTarget=e[l],d&=Fb(e[l],f,!1,c)}return d}return Eb(a, | |
new O(b,this))},wb=function(a){a=a[qb];return a instanceof P?a:null},Gb="__closure_events_fn_"+(1E9*Math.random()>>>0),ub=function(a){if(p(a))return a;a[Gb]||(a[Gb]=function(b){return a.handleEvent(b)});return a[Gb]};var Q=function(){E.call(this);this.A=new P(this);this.kc=this;this.Qa=null};u(Q,E);Q.prototype[jb]=!0;g=Q.prototype;g.addEventListener=function(a,b,c,d){tb(this,a,b,c,d)};g.removeEventListener=function(a,b,c,d){Bb(this,a,b,c,d)}; | |
g.dispatchEvent=function(a){var b,c=this.Qa;if(c){b=[];for(var d=1;c;c=c.Qa)b.push(c),++d}c=this.kc;d=a.type||a;if(n(a))a=new F(a,c);else if(a instanceof F)a.target=a.target||c;else{var e=a;a=new F(d,c);za(a,e)}var e=!0,f;if(b)for(var k=b.length-1;!a.U&&0<=k;k--)f=a.currentTarget=b[k],e=Hb(f,d,!0,a)&&e;a.U||(f=a.currentTarget=c,e=Hb(f,d,!0,a)&&e,a.U||(e=Hb(f,d,!1,a)&&e));if(b)for(k=0;!a.U&&k<b.length;k++)f=a.currentTarget=b[k],e=Hb(f,d,!1,a)&&e;return e}; | |
g.l=function(){Q.L.l.call(this);this.A&&this.A.removeAll(void 0);this.Qa=null};g.listen=function(a,b,c,d){return this.A.add(String(a),b,!1,c,d)};g.bb=function(a,b,c,d){return this.A.add(String(a),b,!0,c,d)};g.Va=function(a,b,c,d){return this.A.remove(String(a),b,c,d)};var Hb=function(a,b,c,d){b=a.A.j[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var k=b[f];if(k&&!k.removed&&k.pa==c){var l=k.O,N=k.sa||k.src;k.qa&&pb(a.A,k);e=!1!==l.call(N,d)&&e}}return e&&0!=d.kb}; | |
Q.prototype.X=function(a,b,c,d){return this.A.X(String(a),b,c,d)};var Ib=function(a){h.setTimeout(function(){throw a;},0)},Jb,Kb=function(){var a=h.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&(a=function(){var a=document.createElement("iframe");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow,a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+"//"+b.location.host,a=r(function(a){if(a.origin== | |
d||a.data==c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!H("Trident")&&!H("MSIE")){var b=new a,c={},d=c;b.port1.onmessage=function(){c=c.next;var a=c.Fb;c.Fb=null;a()};return function(a){d.next={Fb:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("script")?function(a){var b=document.createElement("script");b.onreadystatechange= | |
function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){h.setTimeout(a,0)}};var Qb=function(a,b){Lb||Mb();Nb||(Lb(),Nb=!0);Ob.push(new Pb(a,b))},Lb,Mb=function(){if(h.Promise&&h.Promise.resolve){var a=h.Promise.resolve();Lb=function(){a.then(Rb)}}else Lb=function(){var a=Rb;!p(h.setImmediate)||h.Window&&h.Window.prototype.setImmediate==h.setImmediate?(Jb||(Jb=Kb()),Jb(a)):h.setImmediate(a)}},Nb=!1,Ob=[],Rb=function(){for(;Ob.length;){var a=Ob;Ob=[];for(var b=0;b<a.length;b++){var c=a[b];try{c.zc.call(c.scope)}catch(d){Ib(d)}}}Nb=!1},Pb=function(a,b){this.zc=a;this.scope= | |
b};var Sb=function(a){a.prototype.then=a.prototype.then;a.prototype.$goog_Thenable=!0},Tb=function(a){if(!a)return!1;try{return!!a.$goog_Thenable}catch(b){return!1}};var R=function(a,b){this.m=0;this.v=void 0;this.n=this.o=null;this.ua=this.La=!1;try{var c=this;a.call(b,function(a){Ub(c,2,a)},function(a){Ub(c,3,a)})}catch(d){Ub(this,3,d)}};R.prototype.then=function(a,b,c){return Vb(this,p(a)?a:null,p(b)?b:null,c)};Sb(R);R.prototype.cancel=function(a){0==this.m&&Qb(function(){var b=new Wb(a);Xb(this,b)},this)}; | |
var Xb=function(a,b){if(0==a.m)if(a.o){var c=a.o;if(c.n){for(var d=0,e=-1,f=0,k;k=c.n[f];f++)if(k=k.wa)if(d++,k==a&&(e=f),0<=e&&1<d)break;0<=e&&(0==c.m&&1==d?Xb(c,b):(d=c.n.splice(e,1)[0],Yb(c),d.Ma(b)))}}else Ub(a,3,b)},$b=function(a,b){a.n&&a.n.length||2!=a.m&&3!=a.m||Zb(a);a.n||(a.n=[]);a.n.push(b)},Vb=function(a,b,c,d){var e={wa:null,jb:null,Ma:null};e.wa=new R(function(a,k){e.jb=b?function(c){try{var e=b.call(d,c);a(e)}catch(J){k(J)}}:a;e.Ma=c?function(b){try{var e=c.call(d,b);void 0===e&&b instanceof | |
Wb?k(b):a(e)}catch(J){k(J)}}:k});e.wa.o=a;$b(a,e);return e.wa};R.prototype.vb=function(a){this.m=0;Ub(this,2,a)};R.prototype.wb=function(a){this.m=0;Ub(this,3,a)}; | |
var Ub=function(a,b,c){if(0==a.m){if(a==c)b=3,c=new TypeError("Promise cannot resolve to itself");else{if(Tb(c)){a.m=1;c.then(a.vb,a.wb,a);return}if(q(c))try{var d=c.then;if(p(d)){ac(a,c,d);return}}catch(e){b=3,c=e}}a.v=c;a.m=b;Zb(a);3!=b||c instanceof Wb||bc(a,c)}},ac=function(a,b,c){a.m=1;var d=!1,e=function(b){d||(d=!0,a.vb(b))},f=function(b){d||(d=!0,a.wb(b))};try{c.call(b,e,f)}catch(k){f(k)}},Zb=function(a){a.La||(a.La=!0,Qb(a.uc,a))}; | |
R.prototype.uc=function(){for(;this.n&&this.n.length;){var a=this.n;this.n=[];for(var b=0;b<a.length;b++){var c=a[b],d=this.v;2==this.m?c.jb(d):(Yb(this),c.Ma(d))}}this.La=!1};var Yb=function(a){for(;a&&a.ua;a=a.o)a.ua=!1},bc=function(a,b){a.ua=!0;Qb(function(){a.ua&&cc.call(null,b)})},cc=Ib,Wb=function(a){v.call(this,a)};u(Wb,v);Wb.prototype.name="cancel";/* | |
Portions of this code are from MochiKit, received by | |
The Closure Authors under the MIT license. All other code is Copyright | |
2005-2009 The Closure Authors. All Rights Reserved. | |
*/ | |
var S=function(a,b){this.ja=[];this.hb=a;this.gb=b||null;this.W=this.C=!1;this.v=void 0;this.Ka=this.Lb=this.Ja=!1;this.ka=0;this.o=null;this.Ia=0};S.prototype.cancel=function(a){if(this.C)this.v instanceof S&&this.v.cancel();else{if(this.o){var b=this.o;delete this.o;a?b.cancel(a):(b.Ia--,0>=b.Ia&&b.cancel())}this.hb?this.hb.call(this.gb,this):this.Ka=!0;this.C||this.w(new dc)}};S.prototype.ib=function(a,b){this.Ja=!1;ec(this,a,b)}; | |
var ec=function(a,b,c){a.C=!0;a.v=c;a.W=!b;fc(a)},hc=function(a){if(a.C){if(!a.Ka)throw new gc;a.Ka=!1}};S.prototype.G=function(a){hc(this);ec(this,!0,a)};S.prototype.w=function(a){hc(this);ec(this,!1,a)};S.prototype.J=function(a,b){return ic(this,a,null,b)};var ic=function(a,b,c,d){a.ja.push([b,c,d]);a.C&&fc(a);return a};S.prototype.then=function(a,b,c){var d,e,f=new R(function(a,b){d=a;e=b});ic(this,d,function(a){a instanceof dc?f.cancel():e(a)});return f.then(a,b,c)};Sb(S); | |
var jc=function(a){var b=new S;ic(a,b.G,b.w,b);return b},kc=function(a){return la(a.ja,function(a){return p(a[1])})},fc=function(a){if(a.ka&&a.C&&kc(a)){var b=a.ka,c=lc[b];c&&(h.clearTimeout(c.ma),delete lc[b]);a.ka=0}a.o&&(a.o.Ia--,delete a.o);for(var b=a.v,d=c=!1;a.ja.length&&!a.Ja;){var e=a.ja.shift(),f=e[0],k=e[1],e=e[2];if(f=a.W?k:f)try{var l=f.call(e||a.gb,b);void 0!==l&&(a.W=a.W&&(l==b||l instanceof Error),a.v=b=l);Tb(b)&&(d=!0,a.Ja=!0)}catch(N){b=N,a.W=!0,kc(a)||(c=!0)}}a.v=b;d&&(l=r(a.ib, | |
a,!0),d=r(a.ib,a,!1),b instanceof S?(ic(b,l,d),b.Lb=!0):b.then(l,d));c&&(b=new mc(b),lc[b.ma]=b,a.ka=b.ma)},nc=function(a){var b=new S;b.G(a);return b},pc=function(){var a=oc,b=new S;b.w(a);return b},gc=function(){v.call(this)};u(gc,v);gc.prototype.message="Deferred has already fired";gc.prototype.name="AlreadyCalledError";var dc=function(){v.call(this)};u(dc,v);dc.prototype.message="Deferred was canceled";dc.prototype.name="CanceledError"; | |
var mc=function(a){this.ma=h.setTimeout(r(this.pc,this),0);this.ga=a};mc.prototype.pc=function(){delete lc[this.ma];throw this.ga;};var lc={};var qc=function(a){this.$a=[];this.e=a};qc.prototype.S=function(a){if(!p(a))throw Error("Invalid filter. Must be a function.");this.$a.push(a)};qc.prototype.send=function(a,b){for(var c=new T(a,b),d=0;d<this.$a.length&&(this.$a[d](c),!c.Za);d++);return c.Za?nc():this.e.send(a,b)};var T=function(a,b){this.rc=a;this.qc=b;this.Za=!1};T.prototype.Gb=function(){return this.rc};T.prototype.T=function(){return this.qc};T.prototype.cancel=function(){this.Za=!0};var rc=function(a,b){this.width=a;this.height=b};rc.prototype.clone=function(){return new rc(this.width,this.height)};rc.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};!K&&!I||I&&I&&9<=db||K&&M("1.9.1");I&&M("9");var sc={id:"apiVersion",name:"v",valueType:"text",maxLength:void 0,defaultValue:void 0},tc={id:"appName",name:"an",valueType:"text",maxLength:100,defaultValue:void 0},uc={id:"appVersion",name:"av",valueType:"text",maxLength:100,defaultValue:void 0},vc={id:"clientId",name:"cid",valueType:"text",maxLength:void 0,defaultValue:void 0},wc={id:"language",name:"ul",valueType:"text",maxLength:20,defaultValue:void 0},xc={id:"libVersion",name:"_v",valueType:"text",maxLength:void 0,defaultValue:void 0},yc={id:"sampleRateOverride", | |
name:"usro",valueType:"integer",maxLength:void 0,defaultValue:void 0},zc={id:"screenColors",name:"sd",valueType:"text",maxLength:20,defaultValue:void 0},Ac={id:"screenResolution",name:"sr",valueType:"text",maxLength:20,defaultValue:void 0},Bc={id:"trackingId",name:"tid",valueType:"text",maxLength:void 0,defaultValue:void 0},Cc={id:"viewportSize",name:"vp",valueType:"text",maxLength:20,defaultValue:void 0},Dc={Rc:sc,Uc:tc,Vc:uc,cd:vc,vd:wc,wd:xc,Cd:yc,Dd:zc,Ed:Ac,Qd:Bc,Xd:Cc},Fc=function(a){if(!n(a))return a; | |
var b=Ec(a,Ka);if(q(b))return b;b=Ec(a,Dc);if(q(b))return b;b=/^dimension(\d+)$/.exec(a);if(null!==b)return La(parseInt(b[1],10));b=/^metric(\d+)$/.exec(a);if(null!==b)return Ma(parseInt(b[1],10));throw Error(a+" is not a valid parameter name.");},Ec=function(a,b){var c=xa(b,function(b){return b.id==a&&"metric"!=a&&"dimension"!=a});return q(c)?c:null};var W=function(a,b){this.Zb=b;this.q=b.Sa();this.sb=new C;this.Ya=!1};g=W.prototype;g.set=function(a,b){var c=Fc(a);this.sb.set(c,b)};g.S=function(a){this.Zb.S(a)};g.send=function(a,b){if(a instanceof D)return a.send(this);var c=this.sb.clone();b instanceof C?c.ia(b):q(b)&&ua(b,function(a,b){null!=a&&c.set(Fc(b),a)},this);this.Ya&&(this.Ya=!1,c.set(Ea,"start"));return this.q.send(a,c)};g.Gc=function(a){var b={description:a};this.set(Fa,a);return this.send("appview",b)}; | |
g.Hc=function(a,b,c,d){return this.send("event",{eventCategory:a,eventAction:b,eventLabel:c,eventValue:d})};g.Jc=function(a,b,c){return this.send("social",{socialNetwork:a,socialAction:b,socialTarget:c})};g.Ic=function(a,b){return this.send("exception",{exDescription:a,exFatal:b})};g.Cb=function(a,b,c,d,e){return this.send("timing",{timingCategory:a,timingVar:b,timingLabel:d,timingValue:c,sampleRateOverride:e})};g.Ac=function(){this.Ya=!0};g.Mc=function(a,b,c,d){return new Gc(this,a,b,c,d)}; | |
var Gc=function(a,b,c,d,e){this.yb=a;this.bc=b;this.ec=c;this.cc=d;this.V=e;this.dc=s()};Gc.prototype.send=function(){var a=this.yb.Cb(this.bc,this.ec,s()-this.dc,this.cc,this.V);this.yb=null;return a};var Hc=function(a,b,c,d,e){this.ic=a;this.fc=b;this.gc=c;this.k=d;this.hc=e}; | |
Hc.prototype.Cc=function(a){var b=new W(0,this.hc.create());b.set(xc,this.ic);b.set(sc,1);b.set(tc,this.fc);b.set(uc,this.gc);b.set(Bc,a);a=window.navigator.language;b.set(wc,a);a=screen.colorDepth+"-bit";b.set(zc,a);a=[screen.width,screen.height].join("x");b.set(Ac,a);a=window.document;a="CSS1Compat"==a.compatMode?a.documentElement:a.body;a=new rc(a.clientWidth,a.clientHeight);a=[a.width,a.height].join("x");b.set(Cc,a);return b};Hc.prototype.Bc=function(){return jc(this.k.ha)};var Ic=function(a){this.sc=a};Ic.prototype.send=function(a,b){this.sc.push({Ub:a,Vb:b});return nc()};var Jc=function(a,b,c){this.k=a;this.ra=[];this.M={enabled:new Ic(this.ra),disabled:c};this.q=this.M.enabled;ic(jc(this.k.ha),ha(this.Pb,b),this.Ob,this)};Jc.prototype.Pb=function(a){this.M.enabled=a();Kc(this);ka(this.ra,function(a){this.send(a.Ub,a.Vb)},this);this.ra=null;Lc(this.k,r(this.Xb,this))};Jc.prototype.Ob=function(){this.q=this.M.enabled=this.M.disabled;this.ra=null};Jc.prototype.send=function(a,b){return this.q.send(a,b)};var Kc=function(a){a.q=a.k.va()?a.M.enabled:a.M.disabled}; | |
Jc.prototype.Xb=function(a){switch(a){case "analytics.tracking-permitted":Kc(this)}};var Mc=function(a,b,c,d,e,f){S.call(this,e,f);this.Na=a;this.Oa=[];this.lb=!!b;this.Nb=!!c;this.Mb=!!d;for(b=this.mb=0;b<a.length;b++)ic(a[b],r(this.rb,this,b,!0),r(this.rb,this,b,!1));0!=a.length||this.lb||this.G(this.Oa)};u(Mc,S);Mc.prototype.rb=function(a,b,c){this.mb++;this.Oa[a]=[b,c];this.C||(this.lb&&b?this.G([a,c]):this.Nb&&!b?this.w(c):this.mb==this.Na.length&&this.G(this.Oa));this.Mb&&!b&&(c=null);return c};Mc.prototype.w=function(a){Mc.L.w.call(this,a);for(a=0;a<this.Na.length;a++)this.Na[a].cancel()}; | |
var Nc=function(a){return(new Mc(a,!1,!0)).J(function(a){for(var c=[],d=0;d<a.length;d++)c[d]=a[d][1];return c})};var X=function(a){this.H=a;this.V=100;this.nb=[];this.Pa=this.la=null;this.ha=Oc(this);this.ha.J(function(){tb(this.H,"a",r(this.Rb,this))},this)},Oc=function(a){return Pc(a).J(function(){return this},a)},Pc=function(a){return Nc([Qc(a),Rc(a)])};X.prototype.Rb=function(){var a=this.la,b=this.va();Pc(this).J(function(){if(a!=this.la)throw Error("User ID changed unexpectedly!");b!=this.va()&&Sc(this)},this)};var Lc=function(a,b){a.nb.push(b)}; | |
X.prototype.Lc=function(a){this.H.set("analytics.tracking-permitted",a).J(function(){this.Pa=a},this)};X.prototype.va=function(){var a;if(a=this.Pa)a=h._gaUserPrefs,a=!(a&&a.ioo&&a.ioo());return a}; | |
var Qc=function(a){return a.H.get("analytics.tracking-permitted").J(function(a){this.Pa=void 0!==a?a:!0},a)},Rc=function(a){return a.H.get("analytics.user-id").J(function(a){if(!a){a="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".split("");for(var c=0,d=a.length;c<d;c++)switch(a[c]){case "x":a[c]=Math.floor(16*Math.random()).toString(16);break;case "y":a[c]=(Math.floor(4*Math.random())+8).toString(16)}a=a.join("");this.H.set("analytics.user-id",a)}this.la=a},a)};X.prototype.Kc=function(a){this.V=a}; | |
var Sc=function(a){ka(a.nb,function(a){a("analytics.tracking-permitted")})};var Tc=function(a){Q.call(this);this.Wa=a;this.H=chrome.storage.local;chrome.storage.onChanged.addListener(r(this.nc,this))};u(Tc,Q);Tc.prototype.nc=function(a){Uc(this,a)&&this.dispatchEvent("a")};var Uc=function(a,b){return la(wa(b),function(a){return 0==a.lastIndexOf(this.Wa,0)},a)};Tc.prototype.get=function(a){var b=new S,c=this.Wa+"."+a;this.H.get(c,function(a){var e=chrome.runtime.lastError;e?b.w(e):b.G(a[c])});return b}; | |
Tc.prototype.set=function(a,b){var c=new S,d={};d[this.Wa+"."+a]=b;this.H.set(d,function(){var a=chrome.runtime.lastError;a?c.w(a):c.G()});return c};var Y=function(){};Y.Yb=function(){return Y.Ib?Y.Ib:Y.Ib=new Y};Y.prototype.send=function(){return nc()};var Vc=function(a,b){this.Xa=[];var c=r(function(){this.Aa=new qc(b.Sa());ka(this.Xa,function(a){this.Aa.S(a)},this);this.Xa=null;return this.Aa},this);this.q=new Jc(a,c,Y.Yb())};Vc.prototype.Sa=function(){return this.q};Vc.prototype.S=function(a){this.Aa?this.Aa.S(a):this.Xa.push(a)};var Wc=function(a,b){this.k=a;this.mc=b};Wc.prototype.create=function(){return new Vc(this.k,this.mc)};var Xc=function(a,b){Q.call(this);this.ya=a||1;this.R=b||h;this.Ra=r(this.lc,this);this.Ta=s()};u(Xc,Q);g=Xc.prototype;g.enabled=!1;g.g=null;g.lc=function(){if(this.enabled){var a=s()-this.Ta;0<a&&a<.8*this.ya?this.g=this.R.setTimeout(this.Ra,this.ya-a):(this.g&&(this.R.clearTimeout(this.g),this.g=null),this.dispatchEvent("tick"),this.enabled&&(this.g=this.R.setTimeout(this.Ra,this.ya),this.Ta=s()))}};g.start=function(){this.enabled=!0;this.g||(this.g=this.R.setTimeout(this.Ra,this.ya),this.Ta=s())}; | |
g.stop=function(){this.enabled=!1;this.g&&(this.R.clearTimeout(this.g),this.g=null)};g.l=function(){Xc.L.l.call(this);this.stop();delete this.R};var Yc=function(a,b,c){if(p(a))c&&(a=r(a,c));else if(a&&"function"==typeof a.handleEvent)a=r(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647<b?-1:h.setTimeout(a,b||0)};var Z=function(a){E.call(this);this.Ua=a;this.b={}};u(Z,E);var Zc=[];Z.prototype.listen=function(a,b,c,d){m(b)||(b&&(Zc[0]=b.toString()),b=Zc);for(var e=0;e<b.length;e++){var f=tb(a,b[e],c||this.handleEvent,d||!1,this.Ua||this);if(!f)break;this.b[f.key]=f}return this};Z.prototype.bb=function(a,b,c,d){return $c(this,a,b,c,d)};var $c=function(a,b,c,d,e,f){if(m(c))for(var k=0;k<c.length;k++)$c(a,b,c[k],d,e,f);else{b=Ab(b,c,d||a.handleEvent,e,f||a.Ua||a);if(!b)return a;a.b[b.key]=b}return a}; | |
Z.prototype.Va=function(a,b,c,d,e){if(m(b))for(var f=0;f<b.length;f++)this.Va(a,b[f],c,d,e);else c=c||this.handleEvent,e=e||this.Ua||this,c=ub(c),d=!!d,b=kb(a)?a.X(b,c,d,e):a?(a=wb(a))?a.X(b,c,d,e):null:null,b&&(Cb(b),delete this.b[b.key]);return this};Z.prototype.removeAll=function(){ua(this.b,Cb);this.b={}};Z.prototype.l=function(){Z.L.l.call(this);this.removeAll()};Z.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var ad=function(){Q.call(this);this.ta=new Z(this);gb&&(hb?this.ta.listen(ib?document.body:window,["online","offline"],this.tb):(this.ub=gb?navigator.onLine:!0,this.g=new Xc(250),this.ta.listen(this.g,"tick",this.ac),this.g.start()))};u(ad,Q);ad.prototype.ac=function(){var a=gb?navigator.onLine:!0;a!=this.ub&&(this.ub=a,this.tb())};ad.prototype.tb=function(){this.dispatchEvent((gb?navigator.onLine:1)?"online":"offline")}; | |
ad.prototype.l=function(){ad.L.l.call(this);this.ta.xa();this.ta=null;this.g&&(this.g.xa(),this.g=null)};var bd=function(a,b){this.k=a;this.e=b};bd.prototype.send=function(a,b){b.set(vc,this.k.la);return this.e.send(a,b)};var cd=function(a){this.e=a};cd.prototype.send=function(a,b){dd(b);ed(b);return this.e.send(a,b)};var dd=function(a){Ua(a,function(b,c){void 0!==b.maxLength&&"text"==b.valueType&&0<b.maxLength&&c.length>b.maxLength&&a.set(b,c.substring(0,b.maxLength))})},ed=function(a){Ua(a,function(b,c){void 0!==b.defaultValue&&c==b.defaultValue&&a.remove(b)})};var oc={status:"device-offline",Ba:void 0},fd={status:"rate-limited",Ba:void 0},gd={status:"sampled-out",Ba:void 0},hd={status:"sent",Ba:void 0};var id=function(a,b){this.Wb=a;this.e=b};id.prototype.send=function(a,b){var c;c=this.Wb;var d=c.pb(),e=Math.floor((d-c.ob)*c.Sb);0<e&&(c.$=Math.min(c.$+e,c.Tb),c.ob=d);1>c.$?c=!1:(c.$-=1,c=!0);return c||"item"==a||"transaction"==a?this.e.send(a,b):nc(fd)};var jd=function(){this.$=60;this.Tb=500;this.Sb=5E-4;this.pb=function(){return(new Date).getTime()};this.ob=this.pb()};var kd=function(a,b){this.k=a;this.e=b};kd.prototype.send=function(a,b){var c=b.get(vc),c=parseInt(c.split("-")[1],16),d;"timing"!=a?d=this.k.V:((d=b.get(yc))&&b.remove(yc),d||(d=this.k.V));return c<655.36*d?this.e.send(a,b):nc(gd)};var ld=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/,md=L,nd=function(a,b){if(md){md=!1;var c=h.location;if(c){var d=c.href;if(d&&(d=(d=nd(3,d))?decodeURI(d):d)&&d!=c.hostname)throw md=!0,Error();}}return b.match(ld)[a]||null};var od=function(){};od.prototype.Eb=null;var qd=function(a){var b;(b=a.Eb)||(b={},pd(a)&&(b[0]=!0,b[1]=!0),b=a.Eb=b);return b};var rd,sd=function(){};u(sd,od);var td=function(a){return(a=pd(a))?new ActiveXObject(a):new XMLHttpRequest},pd=function(a){if(!a.Hb&&"undefined"==typeof XMLHttpRequest&&"undefined"!=typeof ActiveXObject){for(var b=["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],c=0;c<b.length;c++){var d=b[c];try{return new ActiveXObject(d),a.Hb=d}catch(e){}}throw Error("Could not create ActiveXObject. ActiveX might be disabled, or MSXML might not be installed");}return a.Hb};rd=new sd;var $=function(a){Q.call(this);this.headers=new x;this.fa=a||null;this.D=!1;this.ca=this.a=null;this.ba=this.Fa="";this.K=this.Ea=this.aa=this.Ha=!1;this.ea=0;this.da=null;this.cb="";this.Ga=this.Kb=!1};u($,Q);var ud=/^https?$/i,vd=["POST","PUT"],wd=[],xd=function(a,b,c){var d=new $;wd.push(d);b&&d.listen("complete",b);d.bb("ready",d.tc);d.send(a,"POST",c,void 0)};$.prototype.tc=function(){this.xa();pa(wd,this)}; | |
$.prototype.send=function(a,b,c,d){if(this.a)throw Error("[goog.net.XhrIo] Object is active with another request="+this.Fa+"; newUri="+a);b=b?b.toUpperCase():"GET";this.Fa=a;this.ba="";this.Ha=!1;this.D=!0;this.a=this.fa?td(this.fa):td(rd);this.ca=this.fa?qd(this.fa):qd(rd);this.a.onreadystatechange=r(this.eb,this);try{this.Ea=!0,this.a.open(b,String(a),!0),this.Ea=!1}catch(e){this.ga(5,e);return}a=c||"";var f=this.headers.clone();d&&Ta(d,function(a,b){f.set(b,a)});d=oa(f.F());c=h.FormData&&a instanceof | |
h.FormData;!(0<=ja(vd,b))||d||c||f.set("Content-Type","application/x-www-form-urlencoded;charset=utf-8");f.forEach(function(a,b){this.a.setRequestHeader(b,a)},this);this.cb&&(this.a.responseType=this.cb);"withCredentials"in this.a&&(this.a.withCredentials=this.Kb);try{yd(this),0<this.ea&&((this.Ga=zd(this.a))?(this.a.timeout=this.ea,this.a.ontimeout=r(this.fb,this)):this.da=Yc(this.fb,this.ea,this)),this.aa=!0,this.a.send(a),this.aa=!1}catch(k){this.ga(5,k)}}; | |
var zd=function(a){return I&&M(9)&&ea(a.timeout)&&void 0!==a.ontimeout},na=function(a){return"content-type"==a.toLowerCase()};$.prototype.fb=function(){"undefined"!=typeof aa&&this.a&&(this.ba="Timed out after "+this.ea+"ms, aborting",this.dispatchEvent("timeout"),this.abort(8))};$.prototype.ga=function(a,b){this.D=!1;this.a&&(this.K=!0,this.a.abort(),this.K=!1);this.ba=b;Ad(this);Bd(this)};var Ad=function(a){a.Ha||(a.Ha=!0,a.dispatchEvent("complete"),a.dispatchEvent("error"))}; | |
$.prototype.abort=function(){this.a&&this.D&&(this.D=!1,this.K=!0,this.a.abort(),this.K=!1,this.dispatchEvent("complete"),this.dispatchEvent("abort"),Bd(this))};$.prototype.l=function(){this.a&&(this.D&&(this.D=!1,this.K=!0,this.a.abort(),this.K=!1),Bd(this,!0));$.L.l.call(this)};$.prototype.eb=function(){this.Y||(this.Ea||this.aa||this.K?Cd(this):this.jc())};$.prototype.jc=function(){Cd(this)}; | |
var Cd=function(a){if(a.D&&"undefined"!=typeof aa&&(!a.ca[1]||4!=Dd(a)||2!=Ed(a)))if(a.aa&&4==Dd(a))Yc(a.eb,0,a);else if(a.dispatchEvent("readystatechange"),4==Dd(a)){a.D=!1;try{var b=Ed(a),c,d;t:switch(b){case 200:case 201:case 202:case 204:case 206:case 304:case 1223:d=!0;break t;default:d=!1}if(!(c=d)){var e;if(e=0===b){var f=nd(1,String(a.Fa));if(!f&&self.location)var k=self.location.protocol,f=k.substr(0,k.length-1);e=!ud.test(f?f.toLowerCase():"")}c=e}if(c)a.dispatchEvent("complete"),a.dispatchEvent("success"); | |
else{var l;try{l=2<Dd(a)?a.a.statusText:""}catch(N){l=""}a.ba=l+" ["+Ed(a)+"]";Ad(a)}}finally{Bd(a)}}},Bd=function(a,b){if(a.a){yd(a);var c=a.a,d=a.ca[0]?ba:null;a.a=null;a.ca=null;b||a.dispatchEvent("ready");try{c.onreadystatechange=d}catch(e){}}},yd=function(a){a.a&&a.Ga&&(a.a.ontimeout=null);ea(a.da)&&(h.clearTimeout(a.da),a.da=null)},Dd=function(a){return a.a?a.a.readyState:0},Ed=function(a){try{return 2<Dd(a)?a.a.status:-1}catch(b){return-1}};var Fd=function(a,b,c){this.r=a||null;this.oc=!!c},Hd=function(a){if(!a.c&&(a.c=new x,a.h=0,a.r))for(var b=a.r.split("&"),c=0;c<b.length;c++){var d=b[c].indexOf("="),e=null,f=null;0<=d?(e=b[c].substring(0,d),f=b[c].substring(d+1)):e=b[c];e=decodeURIComponent(e.replace(/\+/g," "));e=Gd(a,e);a.add(e,f?decodeURIComponent(f.replace(/\+/g," ")):"")}};g=Fd.prototype;g.c=null;g.h=null;g.add=function(a,b){Hd(this);this.r=null;a=Gd(this,a);var c=this.c.get(a);c||this.c.set(a,c=[]);c.push(b);this.h++;return this}; | |
g.remove=function(a){Hd(this);a=Gd(this,a);return this.c.Q(a)?(this.r=null,this.h-=this.c.get(a).length,this.c.remove(a)):!1};g.Q=function(a){Hd(this);a=Gd(this,a);return this.c.Q(a)};g.F=function(){Hd(this);for(var a=this.c.t(),b=this.c.F(),c=[],d=0;d<b.length;d++)for(var e=a[d],f=0;f<e.length;f++)c.push(b[d]);return c};g.t=function(a){Hd(this);var b=[];if(n(a))this.Q(a)&&(b=qa(b,this.c.get(Gd(this,a))));else{a=this.c.t();for(var c=0;c<a.length;c++)b=qa(b,a[c])}return b}; | |
g.set=function(a,b){Hd(this);this.r=null;a=Gd(this,a);this.Q(a)&&(this.h-=this.c.get(a).length);this.c.set(a,[b]);this.h++;return this};g.get=function(a,b){var c=a?this.t(a):[];return 0<c.length?String(c[0]):b};g.toString=function(){if(this.r)return this.r;if(!this.c)return"";for(var a=[],b=this.c.F(),c=0;c<b.length;c++)for(var d=b[c],e=encodeURIComponent(String(d)),d=this.t(d),f=0;f<d.length;f++){var k=e;""!==d[f]&&(k+="="+encodeURIComponent(String(d[f])));a.push(k)}return this.r=a.join("&")}; | |
g.clone=function(){var a=new Fd;a.r=this.r;this.c&&(a.c=this.c.clone(),a.h=this.h);return a};var Gd=function(a,b){var c=String(b);a.oc&&(c=c.toLowerCase());return c};var Id=function(a,b){this.$b=a;this.na=b};Id.prototype.send=function(a,b){if(gb&&!navigator.onLine)return pc();var c=new S,d=Jd(a,b);d.length>this.na?c.w({status:"payload-too-big",Ba:Ra("Encoded hit length == %s, but should be <= %s.",d.length,this.na)}):xd(this.$b,function(){c.G(hd)},d);return c};var Jd=function(a,b){var c=new Fd;c.add(Da.name,a);Ua(b,function(a,b){c.add(a.name,b.toString())});return c.toString()};var Kd=function(a,b,c){this.k=a;this.Qb=b;this.na=c};Kd.prototype.Sa=function(){if(!this.q){var a=this.k;if(!jc(a.ha).C)throw Error("Cannot construct shared channel prior to settings being ready.");new ad;var b=new cd(new Id(this.Qb,this.na)),c=new jd;this.q=new bd(a,new kd(a,new id(c,b)))}return this.q};var Ld=new x,Md=function(){if(!Ba){var a=new Tc("google-analytics");Ba=new X(a)}return Ba};t("goog.async.Deferred",S);t("goog.async.Deferred.prototype.addCallback",S.prototype.J);t("goog.events.EventTarget",Q);t("goog.events.EventTarget.prototype.listen",Q.prototype.listen);t("analytics.getService",function(a){var b=Ld.get(a,null);if(null===b){var b=chrome.runtime.getManifest().version,c=Md();if(!Ca){var d=Md();Ca=new Wc(d,new Kd(d,"https://www.google-analytics.com/collect",8192))}b=new Hc("ca1.5.2",a,b,c,Ca);Ld.set(a,b)}return b});t("analytics.internal.GoogleAnalyticsService",Hc); | |
t("analytics.internal.GoogleAnalyticsService.prototype.getTracker",Hc.prototype.Cc);t("analytics.internal.GoogleAnalyticsService.prototype.getConfig",Hc.prototype.Bc);t("analytics.internal.ServiceSettings",X);t("analytics.internal.ServiceSettings.prototype.setTrackingPermitted",X.prototype.Lc);t("analytics.internal.ServiceSettings.prototype.isTrackingPermitted",X.prototype.va);t("analytics.internal.ServiceSettings.prototype.setSampleRate",X.prototype.Kc);t("analytics.internal.ServiceTracker",W); | |
t("analytics.internal.ServiceTracker.prototype.send",W.prototype.send);t("analytics.internal.ServiceTracker.prototype.sendAppView",W.prototype.Gc);t("analytics.internal.ServiceTracker.prototype.sendEvent",W.prototype.Hc);t("analytics.internal.ServiceTracker.prototype.sendSocial",W.prototype.Jc);t("analytics.internal.ServiceTracker.prototype.sendException",W.prototype.Ic);t("analytics.internal.ServiceTracker.prototype.sendTiming",W.prototype.Cb); | |
t("analytics.internal.ServiceTracker.prototype.startTiming",W.prototype.Mc);t("analytics.internal.ServiceTracker.Timing",Gc);t("analytics.internal.ServiceTracker.Timing.prototype.send",Gc.prototype.send);t("analytics.internal.ServiceTracker.prototype.forceSessionStart",W.prototype.Ac);t("analytics.internal.ServiceTracker.prototype.addFilter",W.prototype.S);t("analytics.internal.FilterChannel.Hit",T);t("analytics.internal.FilterChannel.Hit.prototype.getHitType",T.prototype.Gb); | |
t("analytics.internal.FilterChannel.Hit.prototype.getParameters",T.prototype.T);t("analytics.internal.FilterChannel.Hit.prototype.cancel",T.prototype.cancel);t("analytics.ParameterMap",C);t("analytics.ParameterMap.Entry",C.Entry);t("analytics.ParameterMap.prototype.set",C.prototype.set);t("analytics.ParameterMap.prototype.get",C.prototype.get);t("analytics.ParameterMap.prototype.remove",C.prototype.remove);t("analytics.ParameterMap.prototype.toObject",C.prototype.Jb); | |
t("analytics.HitTypes.APPVIEW","appview");t("analytics.HitTypes.EVENT","event");t("analytics.HitTypes.SOCIAL","social");t("analytics.HitTypes.TRANSACTION","transaction");t("analytics.HitTypes.ITEM","item");t("analytics.HitTypes.TIMING","timing");t("analytics.HitTypes.EXCEPTION","exception");ua(Ka,function(a){var b=a.id.replace(/[A-Z]/,"_$&").toUpperCase();t("analytics.Parameters."+b,a)});t("analytics.filters.EventLabelerBuilder",A); | |
t("analytics.filters.EventLabelerBuilder.prototype.appendToExistingLabel",A.prototype.wc);t("analytics.filters.EventLabelerBuilder.prototype.stripValue",A.prototype.Nc);t("analytics.filters.EventLabelerBuilder.prototype.powersOfTwo",A.prototype.Ec);t("analytics.filters.EventLabelerBuilder.prototype.rangeBounds",A.prototype.Fc);t("analytics.filters.EventLabelerBuilder.prototype.build",A.prototype.Ca);t("analytics.filters.FilterBuilder",z);t("analytics.filters.FilterBuilder.builder",Pa); | |
t("analytics.filters.FilterBuilder.prototype.when",z.prototype.when);t("analytics.filters.FilterBuilder.prototype.whenHitType",z.prototype.zb);t("analytics.filters.FilterBuilder.prototype.whenValue",z.prototype.Oc);t("analytics.filters.FilterBuilder.prototype.applyFilter",z.prototype.xb);t("analytics.filters.FilterBuilder.prototype.build",z.prototype.Ca);t("analytics.EventBuilder",D);t("analytics.EventBuilder.builder",function(){return Va});t("analytics.EventBuilder.prototype.category",D.prototype.xc); | |
t("analytics.EventBuilder.prototype.action",D.prototype.action);t("analytics.EventBuilder.prototype.label",D.prototype.label);t("analytics.EventBuilder.prototype.value",D.prototype.value);t("analytics.EventBuilder.prototype.dimension",D.prototype.yc);t("analytics.EventBuilder.prototype.metric",D.prototype.Dc);t("analytics.EventBuilder.prototype.send",D.prototype.send); })() | |
},{}],4:[function(require,module,exports){ | |
var Util = require('./util.js'); | |
var GET_USER_INFO_URL = | |
'https://clients1.google.com/tbproxy/getaccountinfo?rv=2'; | |
var LOGIN_URL = 'https://accounts.google.com/'; | |
var LOGIN_URL_PATTERN = 'https://accounts.google.com/*'; | |
var COOKIE_NAME = 'LSID'; | |
var COOKIE_URL = 'https://accounts.google.com'; | |
function IdentityManager() { | |
this.name = null; | |
this.picture = null; | |
this.getOrFetchUserInfo(function(userInfo) { | |
//console.log('Fetched userInfo', userInfo); | |
}); | |
} | |
/** | |
* If the cookie has changed, fetch the user info. Otherwise, get it from | |
* the cached storage. | |
*/ | |
IdentityManager.prototype.getOrFetchUserInfo = function(callback) { | |
this.hasCookieChanged_(function(isChanged) { | |
if (isChanged) { | |
this.fetchUserInfo_(callback); | |
} else { | |
this.getUserInfo_(callback); | |
} | |
}.bind(this)); | |
}; | |
IdentityManager.prototype.promptLogin = function() { | |
// Show the login tab. If it's already open, focus it. | |
Util.openOrFocusUrl(LOGIN_URL, LOGIN_URL_PATTERN); | |
}; | |
/**** PRIVATE ****/ | |
IdentityManager.prototype.hasCookieChanged_ = function(callback) { | |
var details = { | |
url: COOKIE_URL, | |
name: COOKIE_NAME | |
}; | |
chrome.cookies.get(details, function(cookie) { | |
var currentValue = cookie && cookie.value; | |
// If the current cookie is invalid, automatically callback true. | |
if (!currentValue) { | |
callback(true); | |
return; | |
} | |
// Check if the value has changed WRT the last known one). | |
chrome.storage.local.get([COOKIE_NAME], function(result) { | |
var lastValue = (COOKIE_NAME in result ? result[COOKIE_NAME] : null); | |
var isChanged = (currentValue != lastValue) | |
// Callback informing whether or not the cookie changed. | |
callback(isChanged); | |
// Save the new value of the cookie. | |
if (isChanged) { | |
var items = {}; | |
items[COOKIE_NAME] = currentValue; | |
chrome.storage.local.set(items); | |
} | |
}); | |
}); | |
}; | |
IdentityManager.prototype.getUserInfo_ = function(callback) { | |
// Get the cached user information. | |
chrome.storage.local.get(['name', 'picture', 'email'], function(result) { | |
//console.log('getUserInfo_', result); | |
if (result.name && result.picture) { | |
callback(result); | |
} else { | |
callback(null); | |
} | |
}.bind(this)); | |
}; | |
IdentityManager.prototype.fetchUserInfo_ = function(callback) { | |
// Get user info from the endpoint. | |
var xhr = new XMLHttpRequest(); | |
xhr.open('GET', GET_USER_INFO_URL, true); | |
xhr.onload = function(e) { | |
if (xhr.status == 200) { | |
var json = JSON.parse(xhr.responseText); | |
// Save it via chrome.storage.local. | |
var result = { | |
name: json.display_name, | |
picture: json.profile_picture_url, | |
email: json.email | |
}; | |
chrome.storage.local.set(result); | |
//console.log('fetchUserInfo_.', result); | |
callback(result); | |
} else { | |
// Report an error. | |
console.error('Unable to fetch user info from server.'); | |
callback(null); | |
} | |
}.bind(this); | |
xhr.send(); | |
}; | |
IdentityManager.prototype.monitorGoogleCookies_ = function() { | |
// Listen for changes to the gaia cookies. | |
chrome.cookies.onChanged.addListener(function(changeInfo) { | |
var cookie = changeInfo.cookie; | |
// If the gaia cookie changed, re-fetch the user info. | |
if (cookie.name == COOKIE_NAME && cookie.domain == COOKIE_URL) { | |
// Get the last value of the LSID cookie. | |
chrome.storage.local.get([COOKIE_NAME], function(result) { | |
//console.log('Removed', changeInfo.removed); | |
//console.log('Cookie value', cookie.value); | |
var lastLSIDValue = result[COOKIE_NAME]; | |
if (cookie.value != lastLSIDValue) { | |
// If the cookie has changed, so we should re-fetch user info. | |
// TODO(smus): Sometimes cookies take a while to set, so wait | |
// before making the request. | |
setTimeout(this.fetchUserInfo.bind(this), 200); | |
} | |
}); | |
var items = {}; | |
items[COOKIE_NAME] = cookie.value; | |
chrome.storage.local.set(items); | |
} | |
}.bind(this)); | |
}; | |
module.exports = IdentityManager; | |
},{"./util.js":11}],5:[function(require,module,exports){ | |
require('./deps/google-analytics-bundle.js'); | |
var BatteryLogger = require('./battery-logger.js'); | |
var analyticsService = analytics.getService('shout_out'); | |
tracker = analyticsService.getTracker('UA-35315454-5'); | |
// Maximum lifespan of the client ID is 63 days. Use 60 days just to be safe. | |
var CLIENT_ID_LIFE_MS = (1000 * 60 * 60 * 24) * 60; | |
function Log(params) { | |
params = params || {}; | |
this.onlyAnalytics = !!params.onlyAnalytics; | |
this.LOG_URL = 'http://shout-out-server.appspot.com/log'; | |
this.batteryLogger = new BatteryLogger(); | |
this.clientId = null; | |
this.getOrCreateClientId_(); | |
// Test group for this type of user. Can be reset externally. | |
this.testGroup = 'default'; | |
this.setupAnalyticsListener(); | |
} | |
Log.prototype.logInstalled = function() { | |
var version = this.getVersion_(); | |
this.logData_('installed', { | |
client_time: new Date().valueOf(), | |
client_id: this.clientId, | |
os: navigator.userAgent, | |
version: version | |
}); | |
this.sendEvent('Admin', 'Installed', version); | |
this.consoleLog('Installed'); | |
}; | |
Log.prototype.logUpdated = function(previousVersion) { | |
var version = this.getVersion_(); | |
this.logData_('updated', { | |
client_time: new Date().valueOf(), | |
client_id: this.clientId, | |
os: navigator.userAgent, | |
version: version, | |
previous_version: previousVersion | |
}); | |
var verString = previousVersion + ' => ' + version; | |
this.sendEvent('Admin', 'Updated', verString); | |
this.consoleLog('Updated:', verString); | |
}; | |
Log.prototype.logEnabled = function(is_enabled) { | |
var version = this.getVersion_(); | |
this.logData_('enabled', { | |
client_time: new Date().valueOf(), | |
client_id: this.clientId, | |
os: navigator.userAgent, | |
version: version, | |
is_enabled: is_enabled | |
}); | |
if (is_enabled) { | |
this.sendEvent('Admin', 'Enabled'); | |
} else { | |
this.sendEvent('Admin', 'Disabled'); | |
} | |
this.consoleLog('Enabled:', is_enabled); | |
}; | |
Log.prototype.logSent = function(token, url, verified) { | |
var version = this.getVersion_(); | |
var tld = new URL(url).hostname; | |
this.logData_('sent', { | |
client_time: new Date().valueOf(), | |
client_id: this.clientId, | |
os: navigator.userAgent, | |
version: version, | |
token: token, | |
domain_tld: tld, | |
verified: verified | |
}); | |
// Client side logging. | |
if (verified) { | |
this.sendEvent('Data', 'Sent Verified', tld); | |
} else { | |
this.sendEvent('Data', 'Sent Unverified', tld); | |
} | |
this.consoleLog('Sent token:', token, 'tld:', tld, 'verified:', verified); | |
}; | |
Log.prototype.logReceived = function(token, url) { | |
var tld = new URL(url).hostname; | |
var version = this.getVersion_(); | |
this.logData_('received', { | |
client_time: new Date().valueOf(), | |
client_id: this.clientId, | |
os: navigator.userAgent, | |
version: version, | |
token: token | |
}); | |
this.sendEvent('Data', 'Received', tld); | |
this.consoleLog('Received token:', token, 'tld:', tld); | |
}; | |
Log.prototype.logError = function(reason, opt_extra) { | |
var version = this.getVersion_(); | |
this.logData_('error', { | |
client_time: new Date().valueOf(), | |
client_id: this.clientId, | |
os: navigator.userAgent, | |
version: version, | |
reason: reason | |
}); | |
var extra = opt_extra || version; | |
this.sendEvent('Error', reason, extra); | |
this.consoleLog('Error:', reason); | |
}; | |
Log.prototype.consoleLog = function() { | |
// Handle variable args. | |
var args = [].slice.call(arguments) | |
args.unshift('[L]'); | |
console.log.apply(console, args); | |
}; | |
/**** Private ****/ | |
// Get rid of this for copresence. | |
Log.prototype.logData_ = function(eventType, fields) { | |
if (this.onlyAnalytics) { | |
return; | |
} | |
var data = { | |
type: eventType | |
}; | |
// Set fields | |
if (fields !== undefined) { | |
for (var key in fields) { | |
data[key] = fields[key]; | |
} | |
} | |
// Make a request to the logging server. | |
var xhr = new XMLHttpRequest(); | |
xhr.open('POST', this.LOG_URL, true); | |
xhr.onload = function(e) { | |
if (xhr.status == 200) { | |
this.onLogSuccess_(xhr.responseText); | |
} else { | |
this.onLogError_(xhr.responseText); | |
} | |
}.bind(this); | |
xhr.send(JSON.stringify(data)); | |
}; | |
Log.prototype.getOrCreateClientId_ = function() { | |
// Check the chrome storage API for the existence of a client ID. | |
chrome.storage.local.get(['clientId', 'clientIdTime'], function(result) { | |
var elapsed = new Date() - (result.clientIdTime || 0); | |
// If there's no client ID, or it's expired, we need to make a new one. | |
if (!result.clientId || elapsed > CLIENT_ID_LIFE_MS) { | |
var newClientId = this.generateClientId_(); | |
chrome.storage.local.set({ | |
clientId: newClientId, | |
clientIdTime: new Date().valueOf() | |
}); | |
this.clientId = newClientId; | |
this.consoleLog('Generated new client ID', this.clientId); | |
} else { | |
this.clientId = result.clientId; | |
this.consoleLog('Using existing client ID', this.clientId); | |
} | |
}.bind(this)); | |
}; | |
Log.prototype.generateClientId_ = function(e) { | |
// From http://goo.gl/z2RxK: | |
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); | |
return v.toString(16); | |
}); | |
}; | |
Log.prototype.onLogSuccess_ = function(e) { | |
//this.consoleLog('Logged', e); | |
}; | |
Log.prototype.onLogError_ = function(e) { | |
console.error('Failed to log', e); | |
}; | |
Log.prototype.getVersion_ = function() { | |
return chrome.runtime.getManifest().version; | |
}; | |
Log.prototype.setTestGroup = function(testGroup) { | |
this.testGroup = testGroup; | |
}; | |
Log.prototype.sendEvent = function(category, action, label) { | |
var event = analytics.EventBuilder.builder() | |
.category(category) | |
.action(action) | |
.label(label) | |
.dimension(1, this.clientId) | |
.dimension(2, this.getVersion_()) | |
.dimension(3, this.testGroup); | |
if (this.batteryLogger.isReadyToReport()) { | |
event = event.metric(1, this.batteryLogger.getDischargeRate()) | |
.metric(2, 1); | |
// Take a snapshot of the current battery state. | |
this.batteryLogger.snapshot(); | |
} | |
tracker.send(event); | |
}; | |
/** | |
* Adds a filter that captures hits being sent to Google Analytics. | |
*/ | |
Log.prototype.setupAnalyticsListener = function() { | |
var filter = analytics.filters.FilterBuilder.builder() | |
.whenHitType(analytics.HitTypes.EVENT) | |
.applyFilter(this.onAnalyticsEvent.bind(this)); | |
// Listen to all hits to the analytics server. | |
tracker.addFilter(filter.build()); | |
} | |
Log.prototype.onAnalyticsEvent = function(hit) { | |
var params = hit.getParameters(); | |
var category = params.get(analytics.Parameters.EVENT_CATEGORY); | |
var action = params.get(analytics.Parameters.EVENT_ACTION); | |
this.consoleLog('Sent to analytics', category, action); | |
}; | |
module.exports = Log; | |
},{"./battery-logger.js":1,"./deps/google-analytics-bundle.js":3}],6:[function(require,module,exports){ | |
var IdentityManager = require('./identity-manager.js'); | |
var NotificationManager = require('./notification-manager.js'); | |
var PageManager = require('./page-manager.js'); | |
var StateMachine = require('./state-machine.js'); | |
var Util = require('./util.js'); | |
var LogClient = require('./log-client.js'); | |
var CopresenceManager = require('./copresence-manager.js'); | |
// Notification manager to show notifications. | |
notificationManager = new NotificationManager(); | |
// Create a custom broadcast/receive manager for copresence. | |
copresenceManager = new CopresenceManager(); | |
copresenceManager.on('sent', onSent); | |
copresenceManager.on('received', onReceived); | |
copresenceManager.on('error', onError); | |
// Initialize the state machine. | |
stateMachine = new StateMachine(); | |
stateMachine.on('change', onStateChange); | |
// Page manager opens various Shout Out pages. | |
pageManager = new PageManager(); | |
// Identity manager determines currently logged in user. | |
identityManager = new IdentityManager(); | |
// Log client logs details to Google Analytics. | |
logClient = new LogClient({onlyAnalytics: true}); | |
// Hook up the browser action button. | |
chrome.browserAction.onClicked.addListener(onBrowserAction); | |
// At install-time, log whether or not we have an install or an update. | |
chrome.runtime.onInstalled.addListener(function(details) { | |
var thisVersion = chrome.runtime.getManifest().version; | |
if (details.reason == 'install') { | |
// Open up the welcome page. | |
pageManager.openPage('installed'); | |
logClient.logInstalled(); | |
} else if (details.reason == 'update') { | |
if (thisVersion != details.previousVersion) { | |
logClient.logUpdated(details.previousVersion); | |
} | |
} | |
}); | |
getTestGroup(function(testGroup) { | |
logClient.setTestGroup(testGroup); | |
}); | |
function onSent(isVerified) { | |
console.log('onSent', isVerified); | |
var url = copresenceManager.getLastUrl(); | |
if (isVerified) { | |
stateMachine.success(); | |
logClient.logSent(null, url, true); | |
// Special case: we are on the installed.html page. | |
Util.getCurrentUrl(function(url) { | |
if (url == pageManager.getUrl('installed')) { | |
// Navigate to the success.html URL. | |
pageManager.openPage('success', true); | |
} | |
}); | |
} else { | |
stateMachine.error(); | |
notificationManager.onSendFailAudio(); | |
logClient.logSent(null, url, false); | |
} | |
} | |
function onError(code) { | |
console.log('onError', code); | |
if (code == CopresenceManager.Error.PUBLISH_FAILED) { | |
notificationManager.onSendFailNetwork(); | |
stateMachine.error(); | |
logClient.logError('Publish failed'); | |
} | |
} | |
function onReceived(json) { | |
console.log('onReceived', json); | |
var url = json.url; | |
var title = chrome.i18n.getMessage('notification_receive_title_format') | |
.replace('{{username}}', json.name); | |
var description; | |
if (url == pageManager.getUrl('installed')) { | |
// Handle the special case that we've received the test page. | |
description = chrome.i18n.getMessage('notification_receive_test_page_message'); | |
} else { | |
// Handle the case where we have a regular Shout Out. | |
var pageTitle = json.title ? '\'' + Util.shortenTitle(json.title) + '\'' : 'page'; | |
description = chrome.i18n.getMessage('notification_receive_message_format') | |
.replace('{{title}}', pageTitle) | |
.replace('{{domain}}', Util.getDomainName(url)); | |
} | |
notificationManager.onReceive(url, title, description, json.picture); | |
logClient.logReceived(null, url); | |
} | |
function onBrowserAction(tab) { | |
var state = stateMachine.getState(); | |
if (state == StateMachine.State.READY) { | |
sendShoutOut(tab); | |
} else if (state == StateMachine.State.DISABLED) { | |
pageManager.openPage('settings'); | |
} | |
} | |
function onStateChange(newState, oldState) { | |
if (newState == StateMachine.State.INITIALIZING) { | |
// Subscribe to copresence. | |
copresenceManager.subscribe(); | |
stateMachine.initialized(); | |
} else if (newState == StateMachine.State.DISABLED) { | |
// Unsubscribe from copresence. | |
copresenceManager.unsubscribe(); | |
} | |
// Log when Shout Out is disabled/enabled. | |
if (newState == StateMachine.State.DISABLED) { | |
logClient.logEnabled(false); | |
} else if (oldState == StateMachine.State.DISABLED) { | |
logClient.logEnabled(true); | |
} | |
} | |
function sendShoutOut(tab) { | |
identityManager.getOrFetchUserInfo(function(userInfo) { | |
if (!userInfo) { | |
identityManager.promptLogin(); | |
return; | |
} | |
copresenceManager.send(tab, userInfo); | |
stateMachine.sending(); | |
}); | |
} | |
/** | |
* Separate internal (google.com) folks from everybody else | |
* for analytics purposes. | |
*/ | |
function getTestGroup(callback) { | |
identityManager.getOrFetchUserInfo(function(userInfo) { | |
if (userInfo && userInfo.email && | |
userInfo.email.indexOf('@google.com') >= 0) { | |
result = 'google'; | |
callback(result); | |
} | |
}); | |
}; | |
},{"./copresence-manager.js":2,"./identity-manager.js":4,"./log-client.js":5,"./notification-manager.js":8,"./page-manager.js":9,"./state-machine.js":10,"./util.js":11}],7:[function(require,module,exports){ | |
var EXPIRY_DURATION = 5000; | |
/** | |
* Responsible for de-duplicating incoming messages. This is necessary | |
* because Shout Out broadcasts for ~1s audible, and ~5s inaudible. The | |
* copresence API doesn't dedupe identical Shouts, so we need to do it. | |
*/ | |
function MessageFilter() { | |
// An array of recent Messages. | |
this.recentMessages = []; | |
} | |
/** | |
* Returns true iff this data is new. | |
*/ | |
MessageFilter.prototype.isNew = function(data) { | |
var message = new Message(data, new Date()); | |
// First, remove all expired messages. | |
this.removeExpired(); | |
// Check if there's an existing message. | |
var existingMessage = this.findPreviousMessage(message); | |
if (!existingMessage) { | |
// No existing message: add it to recent messages. | |
this.recentMessages.push(message); | |
// This is a new message, return true. | |
return true; | |
} | |
return false; | |
}; | |
MessageFilter.prototype.removeExpired = function() { | |
for (var i = 0; i < this.recentMessages.length; i++) { | |
var message = this.recentMessages[i]; | |
if (message.olderThan(EXPIRY_DURATION)) { | |
console.log('Expiring message', message); | |
this.recentMessages.splice(i, 1); | |
// Decrement the index, since the array was re-index due to splice. | |
i -= 1; | |
} | |
} | |
}; | |
MessageFilter.prototype.findPreviousMessage = function(message) { | |
for (var i = 0; i < this.recentMessages.length; i++) { | |
var thisMessage = this.recentMessages[i]; | |
if (thisMessage.equals(message)) { | |
return thisMessage; | |
} | |
} | |
return null; | |
}; | |
function Message(data, time) { | |
this.data = data; | |
this.time = time; | |
} | |
Message.prototype.equals = function(otherMessageTime) { | |
return this.data.url == otherMessageTime.data.url && | |
this.data.name == otherMessageTime.data.name; | |
} | |
Message.prototype.olderThan = function(duration) { | |
var now = new Date(); | |
return now - this.time > duration; | |
}; | |
module.exports = MessageFilter; | |
},{}],8:[function(require,module,exports){ | |
var Util = require('./util.js'); | |
var PageManager = require('./page-manager.js'); | |
var REPORT_URL_ROOT = 'https://docs.google.com/a/google.com/forms/d/17BEfb-JLsiJbsZRjnMmTdaH3EtMj8OpxADR0DayWS9s/viewform?'; | |
var FAIL_NOTIFICATION_DELAY = 3000; | |
var NOTIFICATION_IMAGE_SIZE = 256; | |
var pageManager = new PageManager(); | |
/** | |
* Implements Shout Out notifications for every possible case we care about. | |
*/ | |
function NotificationManager() { | |
// Map of notificationId : callback for handling clicks on the | |
// notification itself. | |
this.notificationCallbacks_ = {}; | |
// Handle clicking the notification itself. | |
chrome.notifications.onClicked.addListener( | |
this.onNotificationClicked_.bind(this)); | |
// Handle clicking the notification buttons. | |
chrome.notifications.onButtonClicked.addListener( | |
this.onNotificationButtonClicked_.bind(this)); | |
} | |
NotificationManager.prototype.onSendFailAudio = function() { | |
var options = { | |
type: 'basic', | |
iconUrl: chrome.extension.getURL('images/notifications/notif_alert_256.png'), | |
title: chrome.i18n.getMessage('notification_send_fail_audio_title'), | |
message: chrome.i18n.getMessage('notification_send_fail_audio_message') | |
}; | |
chrome.notifications.create('', options, function(notificationId) { | |
this.clearNotificationDelay_(notificationId, FAIL_NOTIFICATION_DELAY); | |
this.registerClickCallback_(notificationId, function() { | |
pageManager.openPage('error_audio'); | |
}); | |
}.bind(this)); | |
}; | |
NotificationManager.prototype.onSendFailNetwork = function() { | |
var options = { | |
type: 'basic', | |
iconUrl: chrome.extension.getURL('images/notifications/notif_offline_256.png'), | |
title: chrome.i18n.getMessage('notification_send_fail_network_title'), | |
message: chrome.i18n.getMessage('notification_send_fail_network_message') | |
}; | |
chrome.notifications.create('', options, function(notificationId) { | |
this.registerClickCallback_(notificationId, function() { | |
pageManager.openPage('error_network'); | |
}); | |
}.bind(this)); | |
}; | |
NotificationManager.prototype.onReceive = | |
function(url, title, message, opt_iconUrl) { | |
var iconUrl; | |
if (!opt_iconUrl) { | |
iconUrl = chrome.extension.getURL('images/notifications/notif_anon_256.png'); | |
} else { | |
iconUrl = this.setImageSize_(opt_iconUrl, NOTIFICATION_IMAGE_SIZE); | |
} | |
var options = { | |
type: 'basic', | |
iconUrl: iconUrl, | |
title: title, | |
message: message, | |
priority: 2 | |
}; | |
chrome.notifications.create('', options, function(notificationId) { | |
this.registerClickCallback_(notificationId, function() { | |
Util.openOrFocusUrl(url); | |
}.bind(this)); | |
}.bind(this)); | |
}; | |
/** PRIVATE **/ | |
NotificationManager.prototype.registerClickCallback_ = | |
function(notificationId, callback) { | |
this.notificationCallbacks_[notificationId] = callback; | |
}; | |
NotificationManager.prototype.reportProblem_ = function(opt_filename) { | |
var params = { | |
'entry.1909320881': 'navigator.userAgent', | |
}; | |
if (opt_filename) { | |
params['entry.1608031336'] = opt_filename; | |
} | |
var serializedParams = ''; | |
for (param in params) { | |
serializedParams += (param + '=' + params[param] + '&'); | |
} | |
var url = REPORT_URL_ROOT + serializedParams; | |
chrome.tabs.create({url: url}); | |
}; | |
NotificationManager.prototype.clearNotificationDelay_ = | |
function(notificationId, delay) { | |
setTimeout(function() { | |
chrome.notifications.clear(notificationId, function(wasCleared) { | |
console.log('Cleared notification.'); | |
}); | |
}, delay); | |
} | |
/** | |
* Sets a size on the icon URL. Removes any parameters from the end of the | |
* URL, and adds a size= parameter with the specified size. | |
*/ | |
NotificationManager.prototype.setImageSize_ = function(iconUrl, size) { | |
// Strip off the extra params if needed. | |
var baseUrl = iconUrl; | |
var qIndex = iconUrl.indexOf('?'); | |
if (qIndex != -1) { | |
baseUrl = iconUrl.substring(0, qIndex); | |
} | |
// Some image URLs have sizing specified in another way, for example: | |
// http://lh6.googleusercontent.com/.../s20/p.png. In this case, strip the | |
// extra sNN business. | |
var ALT_SIZE_REGEX = '/s[0-9]+/' | |
var match = baseUrl.match(ALT_SIZE_REGEX) | |
if (match && match.length == 1) { | |
var onlyMatch = match[0]; | |
baseUrl = baseUrl.replace(onlyMatch, '/'); | |
} | |
return baseUrl + '?size=' + size; | |
}; | |
NotificationManager.prototype.onNotificationClicked_ = | |
function(notificationId) { | |
console.log('Notification clicked', notificationId); | |
var callback = this.notificationCallbacks_[notificationId]; | |
if (callback) { | |
callback(); | |
} | |
}; | |
NotificationManager.prototype.onNotificationButtonClicked_ = | |
function(notificationId, buttonIndex) { | |
if (buttonIndex == 0) { // Yes. | |
// Do nothing for now. | |
} else if (buttonIndex == 1) { // No. | |
this.reportProblem_(); | |
} | |
} | |
module.exports = NotificationManager; | |
},{"./page-manager.js":9,"./util.js":11}],9:[function(require,module,exports){ | |
var Util = require('./util.js'); | |
var SUFFIX = '.html'; | |
function PageManager() { | |
this.root = 'chrome-extension://{{id}}/pages/' | |
.replace('{{id}}', chrome.runtime.id); | |
} | |
PageManager.prototype.openPage = function(pageName, replaceCurrent) { | |
var url = this.getUrl(pageName); | |
if (replaceCurrent) { | |
chrome.tabs.update({url: url}); | |
} else { | |
Util.openOrFocusUrl(url); | |
} | |
}; | |
PageManager.prototype.getUrl = function(pageName) { | |
return this.root + pageName + SUFFIX; | |
}; | |
module.exports = PageManager; | |
},{"./util.js":11}],10:[function(require,module,exports){ | |
var State = { | |
INITIALIZING: 'INITIALIZING', | |
READY: 'READY', | |
DISABLED: 'DISABLED', | |
SENDING: 'SENDING', | |
ERROR: 'ERROR', | |
SUCCESS: 'SUCCESS' | |
}; | |
var IconMap = { | |
INITIALIZING: '$SIZE_ready.png', | |
READY: '$SIZE_ready.png', | |
DISABLED: '$SIZE_off.png', | |
SENDING: [ | |
'$SIZE_anim1.png', | |
'$SIZE_anim2.png', | |
'$SIZE_anim3.png', | |
'$SIZE_anim4.png' | |
], | |
ERROR: '$SIZE_fail.png', | |
SUCCESS: '$SIZE_success.png' | |
}; | |
var TIMEOUT = 3000; | |
function StateMachine() { | |
// Set the initial state based on what's in the storage. | |
chrome.storage.local.get(['state'], function(result) { | |
if (result.state == State.DISABLED) { | |
// If storage says we're disabled, disable. | |
this.setState_(result.state); | |
} else if (!this.currentState) { | |
// If no state is set, make sure we use the default one. | |
this.setState_(State.INITIALIZING); | |
} | |
}.bind(this)); | |
// Listen to state changes from the storage (they may come from the | |
// options page). | |
chrome.storage.onChanged.addListener(function(changes, namespace) { | |
if ('state' in changes) { | |
var newState = changes.state.newValue; | |
this.setState_(newState); | |
} | |
}.bind(this)); | |
} | |
StateMachine.prototype.on = function(event, callback) { | |
if (event == 'change') { | |
this.callback = callback; | |
} | |
}; | |
StateMachine.prototype.getState = function() { | |
return this.currentState; | |
}; | |
/** | |
* Sends a shout out (only if currently ready). | |
*/ | |
StateMachine.prototype.sending = function() { | |
if (this.currentState != State.READY) { | |
console.error('Cannot start sending if you weren\'t just ready.'); | |
return; | |
} | |
this.setState_(State.SENDING); | |
}; | |
StateMachine.prototype.error = function() { | |
this.setState_(State.ERROR); | |
this.scheduleStateChange_(State.READY, TIMEOUT); | |
}; | |
StateMachine.prototype.success = function() { | |
// Sanity check: we must have been sending to succeed. | |
if (this.currentState != State.SENDING) { | |
console.error('Cannot succeed unless you were just sending.'); | |
return; | |
} | |
this.setState_(State.SUCCESS); | |
this.scheduleStateChange_(State.READY, TIMEOUT); | |
}; | |
StateMachine.prototype.initialized = function() { | |
this.setState_(State.READY); | |
}; | |
StateMachine.prototype.initializing = function() { | |
this.setState_(State.INITIALIZING); | |
}; | |
/***** PRIVATE *****/ | |
StateMachine.prototype.scheduleStateChange_ = function(targetState, delay) { | |
// Before scheduling another state change, cancel pending ones. | |
this.cancelScheduledStateChange_(); | |
this.timer_ = setTimeout(function() { | |
this.setState_(targetState); | |
}.bind(this), delay); | |
}; | |
StateMachine.prototype.cancelScheduledStateChange_ = function() { | |
clearTimeout(this.timer_); | |
this.timer_ = null; | |
}; | |
StateMachine.prototype.setState_ = function(newState) { | |
if (this.timer_) { | |
//console.error('Warning: setting state while state change pending!'); | |
} | |
if (this.currentState == newState) { | |
return; | |
} | |
// Update the icon. | |
this.updateIcon_(newState); | |
// Update the context menu. | |
this.updateContextMenu_(newState); | |
// Save the state in the storage. | |
var oldState = this.currentState; | |
this.currentState = newState; | |
console.log('setState: ' + newState); | |
// Callback to listeners that care about state changes. | |
if (this.callback) { | |
this.callback(newState, oldState); | |
} | |
}; | |
StateMachine.prototype.updateIcon_ = function(newState) { | |
// Check if the browserAction exists (may be in receive-only mode). | |
if (!chrome.browserAction) { | |
return; | |
} | |
var iconInfo = IconMap[newState]; | |
if (iconInfo instanceof Array) { | |
// Start an animation that goes through the keyframes. | |
this.startAnimation_(newState); | |
} else { | |
// Stop the animation that may be running. | |
this.stopAnimation_(); | |
// Set the static icon based on the base of the icon. | |
var iconBase = 'images/actions/action_' + iconInfo; | |
var small = iconBase.replace('$SIZE', '19'); | |
var big = iconBase.replace('$SIZE', '38'); | |
chrome.browserAction.setIcon({path: {'19': small, '38': big}}); | |
} | |
}; | |
StateMachine.prototype.updateContextMenu_ = function(newState) { | |
// Check for the context menu API. | |
if (!chrome.contextMenus) { | |
return; | |
} | |
// Wipe the context menu of old items. | |
chrome.contextMenus.removeAll(); | |
// If it's the ready state, show the disable item. | |
if (newState == State.READY) { | |
chrome.contextMenus.create({ | |
'title': chrome.i18n.getMessage('context_menu_disable'), | |
'contexts': ['browser_action'], | |
'onclick': function() { | |
chrome.storage.local.set({state: State.DISABLED}); | |
}.bind(this) | |
}); | |
} | |
// If it's the disabled state, show the enable item. | |
if (newState == State.DISABLED) { | |
chrome.contextMenus.create({ | |
'title': chrome.i18n.getMessage('context_menu_enable'), | |
'contexts': ['browser_action'], | |
'onclick': function() { | |
this.setState_(State.INITIALIZING); | |
chrome.storage.local.set({state: State.INITIALIZING}); | |
}.bind(this) | |
}); | |
} | |
}; | |
StateMachine.prototype.startAnimation_ = function(newState) { | |
// Check if the browserAction exists (may be in receive-only mode). | |
if (!chrome.browserAction) { | |
return; | |
} | |
var frame = 0; | |
var iconArray = IconMap[newState]; | |
var frameCount = iconArray.length; | |
var updateAnimation = function() { | |
var iconBase = 'images/actions/action_' + iconArray[frame % frameCount]; | |
var small = iconBase.replace('$SIZE', '19'); | |
var big = iconBase.replace('$SIZE', '38'); | |
chrome.browserAction.setIcon({path: {'19': small, '38': big}}); | |
frame += 1; | |
}; | |
// Immediately start the animation. | |
updateAnimation(); | |
// Also setup a timer to finish the animation. | |
this.animationTimer_ = setInterval(updateAnimation, 1000/5); | |
}; | |
StateMachine.prototype.stopAnimation_ = function() { | |
clearInterval(this.animationTimer_); | |
}; | |
StateMachine.State = State; | |
module.exports = StateMachine; | |
},{}],11:[function(require,module,exports){ | |
Util = {}; | |
Util.openOrFocusUrl = function(url, opt_urlMatchPattern) { | |
var urlPattern = opt_urlMatchPattern || url; | |
// Check if tab is already open for this URL. | |
chrome.tabs.query({url: urlPattern}, function(tabs) { | |
// If it's not open already. | |
if (tabs.length == 0) { | |
// Check to see if there is an active window. | |
chrome.windows.getCurrent({}, function(window) { | |
if (window) { | |
// If there's a window, create a tab in it and focus it. | |
chrome.tabs.create({url: url}); | |
chrome.windows.update(window.id, {focused: true}); | |
} else { | |
// If there's no window, create one that's focused. | |
chrome.windows.create({url: url, focused: true}); | |
} | |
}); | |
} else { | |
// Otherwise, if the url is already open, foreground the open tab. | |
var openTab = tabs[0]; | |
chrome.tabs.update(openTab.id, {active: true}); | |
// Focus the tab's window. | |
chrome.windows.update(openTab.windowId, {focused: true}); | |
} | |
}); | |
}; | |
Util.getDomainName = function(url) { | |
var a = document.createElement('a'); | |
a.href = url; | |
return a.hostname; | |
}; | |
Util.getCurrentTab = function(callback) { | |
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { | |
var tab = tabs[0]; | |
if (!tab || !tab.url) { | |
callback(null); | |
return; | |
} | |
callback(tab); | |
}); | |
}; | |
Util.getCurrentUrl = function(callback) { | |
Util.getCurrentTab(function(tab) { | |
var result = tab && tab.url; | |
callback(result); | |
}); | |
}; | |
/** | |
* Converts from a base64-encoded token into an array of symbols for | |
* whispernet. For example, | |
* FUplv8ok => [1, 5, 4, 10, 6, 5, 11, 15, 12, 10, 2, 4] | |
*/ | |
Util.tokenToSymbols = function(base64) { | |
var decoded = atob(base64) | |
var symbols = []; | |
for (var i = 0; i < decoded.length; i++) { | |
// Take each character and convert it into two nibbles. | |
var char = decoded.charCodeAt(i); | |
var hi = (char & 0xF0) >> 4; | |
var lo = char & 0x0F; | |
symbols.push(parseInt(hi, 10)); | |
symbols.push(parseInt(lo, 10)); | |
} | |
return symbols; | |
}; | |
/** | |
* Converts from an array of whispernet symbols into a base64-encoded token. | |
* For example, | |
* [1, 5, 4, 10, 6, 5, 11, 15, 12, 10, 2, 4] => FUplv8ok | |
*/ | |
Util.symbolsToToken = function(symbols) { | |
var binString = ''; | |
// Given a token string, convert it to a binary string. | |
for (var i = 0; i < symbols.length; i += 2) { | |
var hi = parseInt(symbols[i]); | |
var lo = parseInt(symbols[i + 1]); | |
var char = 0; | |
char |= lo; | |
char |= (hi << 4); | |
binString += String.fromCharCode(char); | |
} | |
return btoa(binString); | |
}; | |
/** | |
* Deep equality checking for arrays. | |
*/ | |
Util.arraysEqual = function(a, b) { | |
if (a === b) return true; | |
if (a == null || b == null) return false; | |
if (a.length != b.length) return false; | |
// If you don't care about the order of the elements inside | |
// the array, you should sort both arrays here. | |
for (var i = 0; i < a.length; ++i) { | |
if (a[i] !== b[i]) return false; | |
} | |
return true; | |
}; | |
var MAX_TITLE_LENGTH = 20; | |
/** | |
* Takes a long title and truncates it to a fixed length, adding ellipsis. | |
*/ | |
Util.shortenTitle = function(longTitle) { | |
if (longTitle.length < MAX_TITLE_LENGTH) { | |
return longTitle; | |
} | |
// Magic number selected based on keeping the notification text under | |
// three lines (tested in Chrome 36 on a Mac). | |
var title = longTitle.substring(0, MAX_TITLE_LENGTH); | |
return title + '...'; | |
}; | |
module.exports = Util; | |
},{}]},{},[6]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment