Skip to content
This repository was archived by the owner on Jan 11, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
17ace17
cp
slobak May 29, 2013
f52ac5a
removed options
slobak May 30, 2013
4397aa3
moved success/error
slobak May 30, 2013
f42933a
fix typekit
slobak May 30, 2013
dc8478a
styling
slobak May 31, 2013
15ee2ff
changing layout, wip
slobak May 31, 2013
401fdb8
fixed API communication
slobak May 31, 2013
d222331
basic typeahead, no keyboard selection yet
slobak May 31, 2013
a9dfbb6
keyboard selection, kinda working
slobak May 31, 2013
97d1b76
better keyb selection
slobak May 31, 2013
e452950
working scrolling on keyboard
slobak May 31, 2013
891936f
Grab favicon url from page and fix some bugs
slobak May 31, 2013
d5e8466
Fix resize bug, and show contacts in order
slobak May 31, 2013
f2c7be9
Fix task creation functionality, add logging
slobak Jun 1, 2013
b0f447a
Load correct typekit for locale
slobak Jun 1, 2013
a6162f3
Checkpoint.
Jun 1, 2013
6e39ad1
More changes.
Jun 1, 2013
150085b
Finalized CSS.
Jun 1, 2013
fd957b6
added logging, default assigned to logged-in user
slobak Jun 1, 2013
55d1b87
fix client name
slobak Jun 1, 2013
79abe6c
fixed initial rendering of assignee
slobak Jun 1, 2013
c6b218c
assignee looks like no one but is self by default
slobak Jun 1, 2013
5fbabd3
cp
slobak Jun 3, 2013
2afbf1a
Alt and Tab hotkeys are now more robust
slobak Jun 3, 2013
82c8dbf
fill in field with selected user's name
slobak Jun 3, 2013
9276a4d
fix dimensions for popup
slobak Jun 3, 2013
ccc52ab
select on hover
slobak Jun 3, 2013
af5324f
Final styling.
Jun 3, 2013
96ac135
Updated comments
slobak Jun 4, 2013
221f12e
Update signup link
slobak Jun 4, 2013
3831898
Log workspace change
slobak Jun 4, 2013
0fb9a95
Increase photo resolution, better for retina display
slobak Jun 4, 2013
334e64d
Fix breakage on workspace change when assignee already selected
slobak Jun 4, 2013
7e3f79e
anonymous man
Jun 4, 2013
4a22c90
Fix scrollwheel scrolling of assignee list
slobak Jun 4, 2013
b4df96f
Merge branch 'launch' of github.com:Asana/Chrome-Extension-Example in…
slobak Jun 4, 2013
b3dbb03
Added silhouette
slobak Jun 4, 2013
9c509c1
Update 128px icon
slobak Jun 5, 2013
965e75e
Update manifest with more appropriate configuration
slobak Jun 5, 2013
5992756
Always put current user first
slobak Jun 5, 2013
fbc435c
Moved to use command API instead of a content script
slobak Jun 6, 2013
8a8c343
reduce content script execution to just getting selection
slobak Jun 6, 2013
558a441
Update README
slobak Jun 6, 2013
03a957f
final fixes.
Jun 7, 2013
cdfcca3
Default assignee to current user, update filtered list when opened, r…
slobak Jun 7, 2013
530704f
Don't focus popup body
slobak Jun 7, 2013
fe27575
Use browser action for command and switch keyboard shortcut to Alt+Sh…
slobak Jun 8, 2013
5e5bbdb
Fix wrapping on completion
slobak Jun 10, 2013
28b4920
Fix broken font
slobak Jun 11, 2013
ab2a0aa
Update version for type fix
slobak Jun 11, 2013
f6cca47
Adding client header to API requests
Oct 18, 2014
e2d9938
Fix banner icon alignment
Oct 18, 2014
973b234
Update icons for visual relaunch (still does not have new styles in U…
Dec 1, 2015
04fe5ae
Add a LICENSE file
chadworth Dec 21, 2015
fb323b0
Merge pull request #10 from chadworth/patch-1
slobak Dec 22, 2015
090e5ed
Reduce tab permission to just what is necessary
Jan 4, 2016
20bd3a2
Changes the user-selection to use type-ahead instead of caching all t…
periodic Jun 14, 2016
5ee06e2
Removes unused user filtering code.
periodic Jun 14, 2016
c6a368c
Removes the XHR abort code since it doesn't actually do anything.
periodic Jun 14, 2016
33d98b6
Cleans up some debugging code and adds a missing semicolon.
Jun 14, 2016
1554560
First pass at updated design: inputs, typeahead, logo, colors
Aug 3, 2016
6e00b74
Further design tweaks - status messages, icons
Aug 3, 2016
bccca04
Bump manifest version
Aug 3, 2016
18aafe6
Update font usage
Aug 3, 2016
90d54f7
Remove typekit permissions and improve icon
Sep 7, 2016
54b35b6
Merge pull request #12 from Asana/quick-redesign
slobak Sep 7, 2016
55f4626
Merges in UI updates.
Nov 14, 2016
e3966c3
Minor fix-ups.
Nov 14, 2016
b20720f
Changes the user-selection to use type-ahead.
periodic Nov 14, 2016
e0123f3
Fixes some style errors.
Nov 14, 2016
083cfd5
Fixes some style errors.
periodic Nov 14, 2016
af5fbdb
Bumps manifest version.
Nov 14, 2016
4d9e1c8
Merge pull request #15 from Asana/drewhaven-bump-manifest
periodic Nov 14, 2016
4c72747
Prevents duplicate click handlers on the button.
periodic Oct 4, 2017
05782e4
Merge pull request #16 from Asana/drewhaven/too-many-tasks
periodic Oct 4, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2013-2015 Asana

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 changes: 3 additions & 4 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ integrates Asana into your web experience in the following ways:

* Creates a button in your button-bar which, when clicked, pops up a
QuickAdd window to create a new task associated with the current web page.
It will populate the task name with the page title by default, and
put the URL in the notes, along with any text you may have selected
when you pressed the button.
You can click a button to populate the task name with the page title and
the URL and current selected text in the notes.

* Installs the special Asana TAB+Q keyboard shortcut. When this key combo
* Installs the special Asana ALT+A keyboard shortcut. When this key combo
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this actually ALT+SHIFT+A?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is in fact ALT+A, that would be most unfortunate, as that key is used by certain international keyboards to write accented characters.

is pressed from any web page, it brings up the same popup.
This functionality will operate on any window opened after the extension
is loaded.
Expand Down
115 changes: 104 additions & 11 deletions api_bridge.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/**
* Functionality to communicate with the Asana API. This should get loaded
* in the "server" portion of the chrome extension because it will make
* HTTP requests and needs cross-domain priveleges.
* HTTP requests and needs cross-domain privileges.
*
* The bridge does not need to use an auth token to connect to
* the API, because since it is a browser extension it can access
* the user's cookies, and can use them to authenticate to the API.
* This capability is specific to browser extensions, and other
* types of applications would have to obtain an auth token to communicate
* with the API.
* the API. Since it is a browser extension it can access the user's cookies
* and can use them to authenticate to the API. This capability is specific
* to browser extensions, and other types of applications would have to obtain
* an auth token to communicate with the API.
*/
Asana.ApiBridge = {

Expand All @@ -17,6 +16,26 @@ Asana.ApiBridge = {
*/
API_VERSION: "1.0",

/**
* @type {Integer} How long an entry stays in the cache.
*/
CACHE_TTL_MS: 15 * 60 * 1000,

/**
* @type {Boolean} Set to true on the server (background page), which will
* actually make the API requests. Clients will just talk to the API
* through the ExtensionServer.
*
*/
is_server: false,

/**
* @type {dict} Map from API path to cache entry for recent GET requests.
* date {Date} When cache entry was last refreshed
* response {*} Cached request.
*/
_cache: {},

/**
* @param opt_options {dict} Options to use; if unspecified will be loaded.
* @return {String} The base URL to use for API requests.
Expand All @@ -37,9 +56,64 @@ Asana.ApiBridge = {
* data {dict} Object representing response of API call, depends on
* method. Only available if response was a 200.
* error {String?} Error message, if there was a problem.
* @param options {dict?}
* miss_cache {Boolean} Do not check cache before requesting
*/
request: function(http_method, path, params, callback) {
var url = this.baseApiUrl() + path;
request: function(http_method, path, params, callback, options) {
var me = this;
http_method = http_method.toUpperCase();

// If we're not the server page, send a message to it to make the
// API request.
if (!me.is_server) {
console.info("Client API Request", http_method, path, params);
chrome.runtime.sendMessage({
type: "api",
method: http_method,
path: path,
params: params,
options: options || {}
}, callback);
return;
}

console.info("Server API Request", http_method, path, params);

// Serve from cache first.
if (!options.miss_cache && http_method === "GET") {
var data = me._readCache(path, new Date());
if (data) {
console.log("Serving request from cache", path);
callback(data);
return;
}
}

// Be polite to Asana API and tell them who we are.
var manifest = chrome.runtime.getManifest();
var client_name = [
"chrome-extension",
chrome.i18n.getMessage("@@extension_id"),
manifest.version,
manifest.name
].join(":");

var url = me.baseApiUrl() + path;
var body_data;
if (http_method === "PUT" || http_method === "POST") {
// POST/PUT request, put params in body
body_data = {
data: params,
options: { client_name: client_name }
};
} else {
// GET/DELETE request, add params as URL parameters.
var url_params = Asana.update({ opt_client_name: client_name }, params);
url += "?" + $.param(url_params);
}

console.log("Making request to API", http_method, url);

chrome.cookies.get({
url: url,
name: 'ticket'
Expand All @@ -59,10 +133,14 @@ Asana.ApiBridge = {
url: url,
timeout: 30000, // 30 second timeout
headers: {
"X-Requested-With": "XMLHttpRequest"
"X-Requested-With": "XMLHttpRequest",
"X-Allow-Asana-Client": "1"
},
accept: "application/json",
success: function(data, status, xhr) {
if (http_method === "GET") {
me._writeCache(path, data, new Date());
}
callback(data);
},
error: function(xhr, status, error) {
Expand All @@ -80,20 +158,35 @@ Asana.ApiBridge = {
}
callback(response);
} else {
callback({ error: error || status });
callback({ errors: [{message: error || status }]});
}
},
xhrFields: {
withCredentials: true
}
};
if (http_method === "POST" || http_method === "PUT") {
attrs.data = JSON.stringify({data: params});
attrs.data = JSON.stringify(body_data);
attrs.dataType = "json";
attrs.processData = false;
attrs.contentType = "application/json";
}
$.ajax(attrs);
});
},

_readCache: function(path, date) {
var entry = this._cache[path];
if (entry && entry.date >= date - this.CACHE_TTL_MS) {
return entry.response;
}
return null;
},

_writeCache: function(path, response, date) {
this._cache[path] = {
response: response,
date: date
};
}
};
66 changes: 65 additions & 1 deletion asana.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,68 @@
/**
* Define the top-level Asana namespace.
*/
Asana = {};
Asana = {

// When popping up a window, the size given is for the content.
// When resizing the same window, the size must include the chrome. Sigh.
CHROME_TITLEBAR_HEIGHT: 24,
// Natural dimensions of popup window. The Chrome popup window adds 10px
// bottom padding, so we must add that as well when considering how tall
// our popup window should be.
POPUP_UI_HEIGHT: 310 + 10,
POPUP_UI_WIDTH: 410,
// Size of popup when expanded to include assignee list.
POPUP_EXPANDED_UI_HEIGHT: 310 + 10 + 129,

// If the modifier key is TAB, amount of time user has from pressing it
// until they can press Q and still get the popup to show up.
QUICK_ADD_WINDOW_MS: 5000


};

/**
* Things borrowed from asana library.
*/


Asana.update = function(to, from) {
for (var k in from) {
to[k] = from[k];
}
return to;
};

Asana.Node = {

/**
* Ensures that the bottom of the element is visible. If it is not then it
* will be scrolled up enough to be visible.
*
* Note: this does not take account of the size of the window. That's ok for
* now because the scrolling element is not the top-level element.
*/
ensureBottomVisible: function(node) {
var el = $(node);
var pos = el.position();
var element_from_point = document.elementFromPoint(
pos.left, pos.top + el.height());
if (element_from_point === null ||
$(element_from_point).closest(node).size() === 0) {
node.scrollIntoView(/*alignWithTop=*/ false);
}
}

};

if (!RegExp.escape) {
// Taken from http://simonwillison.net/2006/Jan/20/escape/
RegExp.escape = function(text, opt_do_not_escape_spaces) {
if (opt_do_not_escape_spaces !== true) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); // nolint
} else {
// only difference is lack of escaping \s
return text.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&"); // nolint
}
};
}
13 changes: 0 additions & 13 deletions background.html

This file was deleted.

26 changes: 26 additions & 0 deletions background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Asana.ExtensionServer.listen();
Asana.ServerModel.startPrimingCache();

// Modify referer header sent to typekit, to allow it to serve to us.
// See http://stackoverflow.com/questions/12631853/google-chrome-extensions-with-typekit-fonts
chrome.webRequest.onBeforeSendHeaders.addListener(function(details) {
var requestHeaders = details.requestHeaders;
for (var i = 0; i < requestHeaders.length; ++i) {
if (requestHeaders[i].name.toLowerCase() === 'referer') {
// The request was certainly not initiated by a Chrome extension...
return;
}
}
// Set Referer
requestHeaders.push({
name: 'referer',
// Host must match the domain in our Typekit kit settings
value: 'https://abkfopjdddhbjkiamjhkmogkcfedcnml'
});
return {
requestHeaders: requestHeaders
};
}, {
urls: ['*://use.typekit.net/*'],
types: ['stylesheet', 'script']
}, ['requestHeaders','blocking']);
25 changes: 11 additions & 14 deletions extension_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,19 @@ Asana.ExtensionServer = {
* requests from page clients, which can't make cross-domain requests.
*/
listen: function() {
var self = this;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
var me = this;

// Mark our Api Bridge as the server side (the one that actually makes
// API requests to Asana vs. just forwarding them to the server window).
Asana.ApiBridge.is_server = true;

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.type === "api") {
// Request to the API. Pass it on to the bridge.
Asana.ApiBridge.api(
request.method, request.path, request.data || {}, sendResponse);

} else if (request.type === "quick_add") {
// QuickAdd request, made from a content window.
// Open up a new popup, and set the request information on its window
// (see popup.html for how it's used)
var popup = window.open(
chrome.extension.getURL('popup.html') + '?external=true',
"asana_quick_add",
"dependent=1,resizable=0,location=0,menubar=0,status=0,toolbar=0,width=410,height=310");
popup.quick_add_request = request;
Asana.ApiBridge.request(
request.method, request.path, request.params, sendResponse,
request.options || {});
return true; // will call sendResponse asynchronously
}
});
}
Expand Down
Binary file modified icon128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icon16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed icon19.png
Binary file not shown.
Binary file added icon48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading