diff options
author | erikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-11 21:29:11 +0000 |
---|---|---|
committer | erikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-11 21:29:11 +0000 |
commit | 5e85b6df11e8a98a7384a9b2a84471e3de85011f (patch) | |
tree | 8bfd4b28fde39723c8963dc6a9256bcf12caa366 /chrome/common | |
parent | 6c8dcc3cb5d7f6b0523fd44b6a72aa42b911a575 (diff) | |
download | chromium_src-5e85b6df11e8a98a7384a9b2a84471e3de85011f.zip chromium_src-5e85b6df11e8a98a7384a9b2a84471e3de85011f.tar.gz chromium_src-5e85b6df11e8a98a7384a9b2a84471e3de85011f.tar.bz2 |
Moving samples into docs/examples.
Also did a little cleanup of a few of the samples.
More cleanup still to go...
BUG=26106
TEST=none
Review URL: http://codereview.chromium.org/390013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31714 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common')
45 files changed, 2627 insertions, 6 deletions
diff --git a/chrome/common/extensions/docs/examples/README.txt b/chrome/common/extensions/docs/examples/README.txt new file mode 100644 index 0000000..834cae2 --- /dev/null +++ b/chrome/common/extensions/docs/examples/README.txt @@ -0,0 +1,6 @@ +Chromium Extension Examples + +The directory structure is as follows: +* api/ - trivial extensions focused on a single API package +* extensions/ - full featured extensions spanning multiple API packages +* tutorials/ - multi-step walkthroughs referenced inline in the docs diff --git a/chrome/common/extensions/docs/examples/api/browserAction/print/background.html b/chrome/common/extensions/docs/examples/api/browserAction/print/background.html new file mode 100644 index 0000000..5b3f930 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/browserAction/print/background.html @@ -0,0 +1,12 @@ +<html> +<head> +<script> + // Called when the user clicks on the browser action. + chrome.browserAction.onClicked.addListener(function(tab) { + var action_url = "javascript:window.print();"; + chrome.tabs.update(tab.id, {url: action_url}); + }); +</script> +</head> +</html> + diff --git a/chrome/common/extensions/docs/examples/api/browserAction/print/manifest.json b/chrome/common/extensions/docs/examples/api/browserAction/print/manifest.json new file mode 100644 index 0000000..0949549 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/browserAction/print/manifest.json @@ -0,0 +1,13 @@ +{ + "name": "Print this page", + "description": "Adds a print button to the browser.", + "version": "1.1", + "background_page": "background.html", + "permissions": [ + "tabs", "http://*/*", "https://*/*" + ], + "browser_action": { + "default_title": "Print this page", + "default_icon": "print_16x16.png" + } +}
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/api/browserAction/print/print_16x16.png b/chrome/common/extensions/docs/examples/api/browserAction/print/print_16x16.png Binary files differnew file mode 100644 index 0000000..d145964 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/browserAction/print/print_16x16.png diff --git a/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/icon.png b/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/icon.png Binary files differnew file mode 100755 index 0000000..1f1c906 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/icon.png diff --git a/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/manifest.json b/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/manifest.json new file mode 100755 index 0000000..83e9052 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "A browser action with a popup that changes the page color.", + "version": "1.0", + "permissions": [ + "tabs", "http://*/*", "https://*/*" + ], + "browser_action": { + "default_title": "Set this page's color.", + "default_icon": "icon.png", + "popup": "popup.html" + } +}
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/popup.html b/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/popup.html new file mode 100644 index 0000000..f82fded --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/browserAction/set_page_color/popup.html @@ -0,0 +1,52 @@ +<style> +body { + overflow: hidden; + margin: 0px; + padding: 0px; + background: white; +} + +div:first-child { + margin-top: 0px; +} + +div { + cursor: pointer; + text-align: center; + padding: 1px 3px; + font: menu; + width: 100px; + margin-top: 1px; + background: #cccccc; +} +div:hover { + background: #aaaaaa; +} +#red { + border: 1px solid red; + color: red; +} +#blue { + border: 1px solid blue; + color: blue; +} +#green { + border: 1px solid green; + color: green; +} +#yellow { + border: 1px solid yellow; + color: yellow; +} +</style> +<script> +function click(color) { + chrome.tabs.executeScript(null, + {code:"document.body.style.backgroundColor='" + color.id + "'"}); + window.close(); +} +</script> +<div onclick="click(this)" id="red">red</div> +<div onclick="click(this)" id="blue">blue</div> +<div onclick="click(this)" id="green">green</div> +<div onclick="click(this)" id="yellow">yellow</div> diff --git a/chrome/common/extensions/docs/examples/api/i18n/cld/background.html b/chrome/common/extensions/docs/examples/api/i18n/cld/background.html new file mode 100644 index 0000000..cfb1fac --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/i18n/cld/background.html @@ -0,0 +1,32 @@ +<!-- +Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this +source code is governed by a BSD-style license that can be found in the +LICENSE file. +--> + +<script> +var selectedId = -1; +function refreshLanguage() { + chrome.tabs.detectLanguage(null, function(language) { + console.log(language); + if (language == " invalid_language_code") + language = "???"; + chrome.browserAction.setBadgeText({"text": language, tabId: selectedId}); + }); +} + +chrome.tabs.onUpdated.addListener(function(tabId, props) { + if (props.status == "complete" && tabId == selectedId) + refreshLanguage(); +}); + +chrome.tabs.onSelectionChanged.addListener(function(tabId, props) { + selectedId = tabId; + refreshLanguage(); +}); + +chrome.tabs.getSelected(null, function(tab) { + selectedId = tab.id; + refreshLanguage(); +}); +</script> diff --git a/chrome/common/extensions/docs/examples/api/i18n/cld/manifest.json b/chrome/common/extensions/docs/examples/api/i18n/cld/manifest.json new file mode 100644 index 0000000..f974838 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/i18n/cld/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "CLD", + "description": "Displays the language of a tab", + "version": "0.1", + "background_page": "background.html", + "permissions": [ + "tabs" + ], + "browser_action": { + "default_name": "Page Language" + } +} diff --git a/chrome/common/extensions/docs/examples/api/override/override_igoogle/manifest.json b/chrome/common/extensions/docs/examples/api/override/override_igoogle/manifest.json new file mode 100644 index 0000000..461da24 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/override/override_igoogle/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "iGoogle new tab page", + "version": "0.1", + "chrome_url_overrides": { + "newtab": "redirect.html" + } +}
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/api/override/override_igoogle/redirect.html b/chrome/common/extensions/docs/examples/api/override/override_igoogle/redirect.html new file mode 100644 index 0000000..35117f4 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/override/override_igoogle/redirect.html @@ -0,0 +1,3 @@ +<head> +<meta http-equiv="refresh"content="0;URL=http://www.google.com/ig"> +</head> diff --git a/chrome/common/extensions/docs/examples/api/pageAction/set_icon/background.html b/chrome/common/extensions/docs/examples/api/pageAction/set_icon/background.html new file mode 100644 index 0000000..9986850 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/pageAction/set_icon/background.html @@ -0,0 +1,69 @@ +<html> +<head> +<script> + var lastTabId = 0; + var tab_clicks = {}; + + chrome.tabs.onSelectionChanged.addListener(function(tabId) { + lastTabId = tabId; + chrome.pageAction.show(lastTabId); + }); + + chrome.tabs.getSelected(null, function(tab) { + lastTabId = tab.id; + chrome.pageAction.show(lastTabId); + }); + + // Called when the user clicks on the page action. + chrome.pageAction.onClicked.addListener(function(tab) { + var clicks = tab_clicks[tab.id] || 0; + chrome.pageAction.setIcon({path: "icon" + (clicks + 1) + ".png", + tabId: tab.id}); + if (clicks % 2) { + chrome.pageAction.show(tab.id); + } else { + chrome.pageAction.hide(tab.id); + setTimeout(function() { chrome.pageAction.show(tab.id); }, 200); + } + chrome.pageAction.setTitle({title: "click:" + clicks, tabId: tab.id}); + + // We only have 2 icons, but cycle through 3 icons to test the + // out-of-bounds index bug. + clicks++; + if (clicks > 3) + clicks = 0; + tab_clicks[tab.id] = clicks; + }); + + var i = 0; + window.setInterval(function() { + var clicks = tab_clicks[lastTabId] || 0; + + // Don't animate while in "click" mode. + if (clicks > 0) return; + + // Don't do anything if we don't have a tab yet. + if (lastTabId == 0) return; + + i++; + chrome.pageAction.setIcon({imageData: draw(i*2, i*4), tabId: lastTabId}); + }, 50); + + function draw(starty, startx) { + var canvas = document.getElementById('canvas'); + var context = canvas.getContext('2d'); + context.clearRect(0, 0, canvas.width, canvas.height); + context.fillStyle = "rgba(0,200,0,255)"; + context.fillRect(startx % 19, starty % 19, 8, 8); + context.fillStyle = "rgba(0,0,200,255)"; + context.fillRect((startx + 5) % 19, (starty + 5) % 19, 8, 8); + context.fillStyle = "rgba(200,0,0,255)"; + context.fillRect((startx + 10) % 19, (starty + 10) % 19, 8, 8); + return context.getImageData(0, 0, 19, 19); + } +</script> +</head> +<body> +<canvas id="canvas" width="19" height="19"></canvas> +</body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/pageAction/set_icon/icon1.png b/chrome/common/extensions/docs/examples/api/pageAction/set_icon/icon1.png Binary files differnew file mode 100755 index 0000000..9a79a46 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/pageAction/set_icon/icon1.png diff --git a/chrome/common/extensions/docs/examples/api/pageAction/set_icon/icon2.png b/chrome/common/extensions/docs/examples/api/pageAction/set_icon/icon2.png Binary files differnew file mode 100755 index 0000000..8d3f710 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/pageAction/set_icon/icon2.png diff --git a/chrome/common/extensions/docs/examples/api/pageAction/set_icon/manifest.json b/chrome/common/extensions/docs/examples/api/pageAction/set_icon/manifest.json new file mode 100644 index 0000000..5a374e8 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/pageAction/set_icon/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Animated Page Action", + "description": "This extension adds an animated browser action to the toolbar.", + "version": "1.0", + "permissions": ["tabs"], + "background_page": "background.html", + "page_action": { + "default_title": "First icon" + } +} diff --git a/chrome/common/extensions/docs/examples/api/tabs/inspector/background.html b/chrome/common/extensions/docs/examples/api/tabs/inspector/background.html new file mode 100644 index 0000000..314dc3c --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/inspector/background.html @@ -0,0 +1,5 @@ +<script> +chrome.browserAction.onClicked.addListener(function(tab) { + chrome.tabs.create({url:chrome.extension.getURL("tabs_api.html")}); +}); +</script>
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/api/tabs/inspector/jstemplate_compiled.js b/chrome/common/extensions/docs/examples/api/tabs/inspector/jstemplate_compiled.js new file mode 100644 index 0000000..2f62b31 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/inspector/jstemplate_compiled.js @@ -0,0 +1,1182 @@ +/** + * @fileoverview This file contains miscellaneous basic functionality. + * + */ + +/** + * Creates a DOM element with the given tag name in the document of the + * owner element. + * + * @param {String} tagName The name of the tag to create. + * @param {Element} owner The intended owner (i.e., parent element) of + * the created element. + * @param {Point} opt_position The top-left corner of the created element. + * @param {Size} opt_size The size of the created element. + * @param {Boolean} opt_noAppend Do not append the new element to the owner. + * @return {Element} The newly created element node. + */ +function createElement(tagName, owner, opt_position, opt_size, opt_noAppend) { + var element = ownerDocument(owner).createElement(tagName); + if (opt_position) { + setPosition(element, opt_position); + } + if (opt_size) { + setSize(element, opt_size); + } + if (owner && !opt_noAppend) { + appendChild(owner, element); + } + + return element; +} + +/** + * Creates a text node with the given value. + * + * @param {String} value The text to place in the new node. + * @param {Element} owner The owner (i.e., parent element) of the new + * text node. + * @return {Text} The newly created text node. + */ +function createTextNode(value, owner) { + var element = ownerDocument(owner).createTextNode(value); + if (owner) { + appendChild(owner, element); + } + return element; +} + +/** + * Returns the document owner of the given element. In particular, + * returns window.document if node is null or the browser does not + * support ownerDocument. + * + * @param {Node} node The node whose ownerDocument is required. + * @returns {Document|Null} The owner document or null if unsupported. + */ +function ownerDocument(node) { + return (node ? node.ownerDocument : null) || document; +} + +/** + * Wrapper function to create CSS units (pixels) string + * + * @param {Number} numPixels Number of pixels, may be floating point. + * @returns {String} Corresponding CSS units string. + */ +function px(numPixels) { + return round(numPixels) + "px"; +} + +/** + * Sets the left and top of the given element to the given point. + * + * @param {Element} element The dom element to manipulate. + * @param {Point} point The desired position. + */ +function setPosition(element, point) { + var style = element.style; + style.position = "absolute"; + style.left = px(point.x); + style.top = px(point.y); +} + +/** + * Sets the width and height style attributes to the given size. + * + * @param {Element} element The dom element to manipulate. + * @param {Size} size The desired size. + */ +function setSize(element, size) { + var style = element.style; + style.width = px(size.width); + style.height = px(size.height); +} + +/** + * Sets display to none. Doing this as a function saves a few bytes for + * the 'style.display' property and the 'none' literal. + * + * @param {Element} node The dom element to manipulate. + */ +function displayNone(node) { + node.style.display = 'none'; +} + +/** + * Sets display to default. + * + * @param {Element} node The dom element to manipulate. + */ +function displayDefault(node) { + node.style.display = ''; +} + +/** + * Appends the given child to the given parent in the DOM + * + * @param {Element} parent The parent dom element. + * @param {Node} child The new child dom node. + */ +function appendChild(parent, child) { + parent.appendChild(child); +} + + +/** + * Wrapper for the eval() builtin function to evaluate expressions and + * obtain their value. It wraps the expression in parentheses such + * that object literals are really evaluated to objects. Without the + * wrapping, they are evaluated as block, and create syntax + * errors. Also protects against other syntax errors in the eval()ed + * code and returns null if the eval throws an exception. + * + * @param {String} expr + * @return {Object|Null} + */ +function jsEval(expr) { + try { + return eval('[' + expr + '][0]'); + } catch (e) { + return null; + } +} + + +/** + * Wrapper for the eval() builtin function to execute statements. This + * guards against exceptions thrown, but doesn't return a + * value. Still, mostly for testability, it returns a boolean to + * indicate whether execution was successful. NOTE: + * javascript's eval semantics is murky in that it confounds + * expression evaluation and statement execution into a single + * construct. Cf. jsEval(). + * + * @param {String} stmt + * @return {Boolean} + */ +function jsExec(stmt) { + try { + eval(stmt); + return true; + } catch (e) { + return false; + } +} + + +/** + * Wrapper for eval with a context. NOTE: The style guide + * deprecates eval, so this is the exception that proves the + * rule. Notice also that since the value of the expression is + * returned rather than assigned to a local variable, one major + * objection aganist the use of the with() statement, namely that + * properties of the with() target override local variables of the + * same name, is void here. + * + * @param {String} expr + * @param {Object} context + * @return {Object|Null} + */ +function jsEvalWith(expr, context) { + try { + with (context) { + return eval('[' + expr + '][0]'); + } + } catch (e) { + return null; + } +} + + +var DOM_ELEMENT_NODE = 1; +var DOM_ATTRIBUTE_NODE = 2; +var DOM_TEXT_NODE = 3; +var DOM_CDATA_SECTION_NODE = 4; +var DOM_ENTITY_REFERENCE_NODE = 5; +var DOM_ENTITY_NODE = 6; +var DOM_PROCESSING_INSTRUCTION_NODE = 7; +var DOM_COMMENT_NODE = 8; +var DOM_DOCUMENT_NODE = 9; +var DOM_DOCUMENT_TYPE_NODE = 10; +var DOM_DOCUMENT_FRAGMENT_NODE = 11; +var DOM_NOTATION_NODE = 12; + +/** + * Traverses the element nodes in the DOM tree underneath the given + * node and finds the first node with elemId, or null if there is no such + * element. Traversal is in depth-first order. + * + * NOTE: The reason this is not combined with the elem() function is + * that the implementations are different. + * elem() is a wrapper for the built-in document.getElementById() function, + * whereas this function performs the traversal itself. + * Modifying elem() to take an optional root node is a possibility, + * but the in-built function would perform better than using our own traversal. + * + * @param {Element} node Root element of subtree to traverse. + * @param {String} elemId The id of the element to search for. + * @return {Element|Null} The corresponding element, or null if not found. + */ +function nodeGetElementById(node, elemId) { + for (var c = node.firstChild; c; c = c.nextSibling) { + if (c.id == elemId) { + return c; + } + if (c.nodeType == DOM_ELEMENT_NODE) { + var n = arguments.callee.call(this, c, elemId); + if (n) { + return n; + } + } + } + return null; +} + + +/** + * Get an attribute from the DOM. Simple redirect, exists to compress code. + * + * @param {Element} node Element to interrogate. + * @param {String} name Name of parameter to extract. + * @return {String} Resulting attribute. + */ +function domGetAttribute(node, name) { + return node.getAttribute(name); +} + +/** + * Set an attribute in the DOM. Simple redirect to compress code. + * + * @param {Element} node Element to interrogate. + * @param {String} name Name of parameter to set. + * @param {String} value Set attribute to this value. + */ +function domSetAttribute(node, name, value) { + node.setAttribute(name, value); +} + +/** + * Remove an attribute from the DOM. Simple redirect to compress code. + * + * @param {Element} node Element to interrogate. + * @param {String} name Name of parameter to remove. + */ +function domRemoveAttribute(node, name) { + node.removeAttribute(name); +} + +/** + * Clone a node in the DOM. + * + * @param {Node} node Node to clone. + * @return {Node} Cloned node. + */ +function domCloneNode(node) { + return node.cloneNode(true); +} + + +/** + * Return a safe string for the className of a node. + * If className is not a string, returns "". + * + * @param {Element} node DOM element to query. + * @return {String} + */ +function domClassName(node) { + return node.className ? "" + node.className : ""; +} + +/** + * Adds a class name to the class attribute of the given node. + * + * @param {Element} node DOM element to modify. + * @param {String} className Class name to add. + */ +function domAddClass(node, className) { + var name = domClassName(node); + if (name) { + var cn = name.split(/\s+/); + var found = false; + for (var i = 0; i < jsLength(cn); ++i) { + if (cn[i] == className) { + found = true; + break; + } + } + + if (!found) { + cn.push(className); + } + + node.className = cn.join(' '); + } else { + node.className = className; + } +} + +/** + * Removes a class name from the class attribute of the given node. + * + * @param {Element} node DOM element to modify. + * @param {String} className Class name to remove. + */ +function domRemoveClass(node, className) { + var c = domClassName(node); + if (!c || c.indexOf(className) == -1) { + return; + } + var cn = c.split(/\s+/); + for (var i = 0; i < jsLength(cn); ++i) { + if (cn[i] == className) { + cn.splice(i--, 1); + } + } + node.className = cn.join(' '); +} + +/** + * Checks if a node belongs to a style class. + * + * @param {Element} node DOM element to test. + * @param {String} className Class name to check for. + * @return {Boolean} Node belongs to style class. + */ +function domTestClass(node, className) { + var cn = domClassName(node).split(/\s+/); + for (var i = 0; i < jsLength(cn); ++i) { + if (cn[i] == className) { + return true; + } + } + return false; +} + +/** + * Inserts a new child before a given sibling. + * + * @param {Node} newChild Node to insert. + * @param {Node} oldChild Sibling node. + * @return {Node} Reference to new child. + */ +function domInsertBefore(newChild, oldChild) { + return oldChild.parentNode.insertBefore(newChild, oldChild); +} + +/** + * Appends a new child to the specified (parent) node. + * + * @param {Element} node Parent element. + * @param {Node} child Child node to append. + * @return {Node} Newly appended node. + */ +function domAppendChild(node, child) { + return node.appendChild(child); +} + +/** + * Remove a new child from the specified (parent) node. + * + * @param {Element} node Parent element. + * @param {Node} child Child node to remove. + * @return {Node} Removed node. + */ +function domRemoveChild(node, child) { + return node.removeChild(child); +} + +/** + * Replaces an old child node with a new child node. + * + * @param {Node} newChild New child to append. + * @param {Node} oldChild Old child to remove. + * @return {Node} Replaced node. + */ +function domReplaceChild(newChild, oldChild) { + return oldChild.parentNode.replaceChild(newChild, oldChild); +} + +/** + * Removes a node from the DOM. + * + * @param {Node} node The node to remove. + * @return {Node} The removed node. + */ +function domRemoveNode(node) { + return domRemoveChild(node.parentNode, node); +} + +/** + * Creates a new text node in the given document. + * + * @param {Document} doc Target document. + * @param {String} text Text composing new text node. + * @return {Text} Newly constructed text node. + */ +function domCreateTextNode(doc, text) { + return doc.createTextNode(text); +} + +/** + * Creates a new node in the given document + * + * @param {Document} doc Target document. + * @param {String} name Name of new element (i.e. the tag name).. + * @return {Element} Newly constructed element. + */ +function domCreateElement(doc, name) { + return doc.createElement(name); +} + +/** + * Creates a new attribute in the given document. + * + * @param {Document} doc Target document. + * @param {String} name Name of new attribute. + * @return {Attr} Newly constructed attribute. + */ +function domCreateAttribute(doc, name) { + return doc.createAttribute(name); +} + +/** + * Creates a new comment in the given document. + * + * @param {Document} doc Target document. + * @param {String} text Comment text. + * @return {Comment} Newly constructed comment. + */ +function domCreateComment(doc, text) { + return doc.createComment(text); +} + +/** + * Creates a document fragment. + * + * @param {Document} doc Target document. + * @return {DocumentFragment} Resulting document fragment node. + */ +function domCreateDocumentFragment(doc) { + return doc.createDocumentFragment(); +} + +/** + * Redirect to document.getElementById + * + * @param {Document} doc Target document. + * @param {String} id Id of requested node. + * @return {Element|Null} Resulting element. + */ +function domGetElementById(doc, id) { + return doc.getElementById(id); +} + +/** + * Redirect to window.setInterval + * + * @param {Window} win Target window. + * @param {Function} fun Callback function. + * @param {Number} time Time in milliseconds. + * @return {Object} Contract id. + */ +function windowSetInterval(win, fun, time) { + return win.setInterval(fun, time); +} + +/** + * Redirect to window.clearInterval + * + * @param {Window} win Target window. + * @param {object} id Contract id. + * @return {any} NOTE: Return type unknown? + */ +function windowClearInterval(win, id) { + return win.clearInterval(id); +} + +/** + * Determines whether one node is recursively contained in another. + * @param parent The parent node. + * @param child The node to look for in parent. + * @return parent recursively contains child + */ +function containsNode(parent, child) { + while (parent != child && child.parentNode) { + child = child.parentNode; + } + return parent == child; +}; +/** + * @fileoverview This file contains javascript utility functions that + * do not depend on anything defined elsewhere. + * + */ + +/** + * Returns the value of the length property of the given object. Used + * to reduce compiled code size. + * + * @param {Array | String} a The string or array to interrogate. + * @return {Number} The value of the length property. + */ +function jsLength(a) { + return a.length; +} + +var min = Math.min; +var max = Math.max; +var ceil = Math.ceil; +var floor = Math.floor; +var round = Math.round; +var abs = Math.abs; + +/** + * Copies all properties from second object to the first. Modifies to. + * + * @param {Object} to The target object. + * @param {Object} from The source object. + */ +function copyProperties(to, from) { + foreachin(from, function(p) { + to[p] = from[p]; + }); +} + +/** + * Iterates over the array, calling the given function for each + * element. + * + * @param {Array} array + * @param {Function} fn + */ +function foreach(array, fn) { + var I = jsLength(array); + for (var i = 0; i < I; ++i) { + fn(array[i], i); + } +} + +/** + * Safely iterates over all properties of the given object, calling + * the given function for each property. If opt_all isn't true, uses + * hasOwnProperty() to assure the property is on the object, not on + * its prototype. + * + * @param {Object} object + * @param {Function} fn + * @param {Boolean} opt_all If true, also iterates over inherited properties. + */ +function foreachin(object, fn, opt_all) { + for (var i in object) { + if (opt_all || !object.hasOwnProperty || object.hasOwnProperty(i)) { + fn(i, object[i]); + } + } +} + +/** + * Appends the second array to the first, copying its elements. + * Optionally only a slice of the second array is copied. + * + * @param {Array} a1 Target array (modified). + * @param {Array} a2 Source array. + * @param {Number} opt_begin Begin of slice of second array (optional). + * @param {Number} opt_end End (exclusive) of slice of second array (optional). + */ +function arrayAppend(a1, a2, opt_begin, opt_end) { + var i0 = opt_begin || 0; + var i1 = opt_end || jsLength(a2); + for (var i = i0; i < i1; ++i) { + a1.push(a2[i]); + } +} + +/** + * Trim whitespace from begin and end of string. + * + * @see testStringTrim(); + * + * @param {String} str Input string. + * @return {String} Trimmed string. + */ +function stringTrim(str) { + return stringTrimRight(stringTrimLeft(str)); +} + +/** + * Trim whitespace from beginning of string. + * + * @see testStringTrimLeft(); + * + * @param {String} str Input string. + * @return {String} Trimmed string. + */ +function stringTrimLeft(str) { + return str.replace(/^\s+/, ""); +} + +/** + * Trim whitespace from end of string. + * + * @see testStringTrimRight(); + * + * @param {String} str Input string. + * @return {String} Trimmed string. + */ +function stringTrimRight(str) { + return str.replace(/\s+$/, ""); +} + +/** + * Jscompiler wrapper for parseInt() with base 10. + * + * @param {String} s String repersentation of a number. + * + * @return {Number} The integer contained in s, converted on base 10. + */ +function parseInt10(s) { + return parseInt(s, 10); +} +/** + * @fileoverview A simple formatter to project JavaScript data into + * HTML templates. The template is edited in place. I.e. in order to + * instantiate a template, clone it from the DOM first, and then + * process the cloned template. This allows for updating of templates: + * If the templates is processed again, changed values are merely + * updated. + * + * NOTE: IE DOM doesn't have importNode(). + * + * NOTE: The property name "length" must not be used in input + * data, see comment in jstSelect_(). + */ + + +/** + * Names of jstemplate attributes. These attributes are attached to + * normal HTML elements and bind expression context data to the HTML + * fragment that is used as template. + */ +var ATT_select = 'jsselect'; +var ATT_instance = 'jsinstance'; +var ATT_display = 'jsdisplay'; +var ATT_values = 'jsvalues'; +var ATT_eval = 'jseval'; +var ATT_transclude = 'transclude'; +var ATT_content = 'jscontent'; + + +/** + * Names of special variables defined by the jstemplate evaluation + * context. These can be used in js expression in jstemplate + * attributes. + */ +var VAR_index = '$index'; +var VAR_this = '$this'; + + +/** + * Context for processing a jstemplate. The context contains a context + * object, whose properties can be referred to in jstemplate + * expressions, and it holds the locally defined variables. + * + * @param {Object} opt_data The context object. Null if no context. + * + * @param {Object} opt_parent The parent context, from which local + * variables are inherited. Normally the context object of the parent + * context is the object whose property the parent object is. Null for the + * context of the root object. + * + * @constructor + */ +function JsExprContext(opt_data, opt_parent) { + var me = this; + + /** + * The local context of the input data in which the jstemplate + * expressions are evaluated. Notice that this is usually an Object, + * but it can also be a scalar value (and then still the expression + * $this can be used to refer to it). Notice this can be a scalar + * value, including undefined. + * + * @type {Object} + */ + me.data_ = opt_data; + + /** + * The context for variable definitions in which the jstemplate + * expressions are evaluated. Other than for the local context, + * which replaces the parent context, variable definitions of the + * parent are inherited. The special variable $this points to data_. + * + * @type {Object} + */ + me.vars_ = {}; + if (opt_parent) { + copyProperties(me.vars_, opt_parent.vars_); + } + this.vars_[VAR_this] = me.data_; +} + + +/** + * Evaluates the given expression in the context of the current + * context object and the current local variables. + * + * @param {String} expr A javascript expression. + * + * @param {Element} template DOM node of the template. + * + * @return The value of that expression. + */ +JsExprContext.prototype.jseval = function(expr, template) { + with (this.vars_) { + with (this.data_) { + try { + return (function() { + return eval('[' + expr + '][0]'); + }).call(template); + } catch (e) { + return null; + } + } + } +} + + +/** + * Clones the current context for a new context object. The cloned + * context has the data object as its context object and the current + * context as its parent context. It also sets the $index variable to + * the given value. This value usually is the position of the data + * object in a list for which a template is instantiated multiply. + * + * @param {Object} data The new context object. + * + * @param {Number} index Position of the new context when multiply + * instantiated. (See implementation of jstSelect().) + * + * @return {JsExprContext} + */ +JsExprContext.prototype.clone = function(data, index) { + var ret = new JsExprContext(data, this); + ret.setVariable(VAR_index, index); + if (this.resolver_) { + ret.setSubTemplateResolver(this.resolver_); + } + return ret; +} + + +/** + * Binds a local variable to the given value. If set from jstemplate + * jsvalue expressions, variable names must start with $, but in the + * API they only have to be valid javascript identifier. + * + * @param {String} name + * + * @param {Object} value + */ +JsExprContext.prototype.setVariable = function(name, value) { + this.vars_[name] = value; +} + + +/** + * Sets the function used to resolve the values of the transclude + * attribute into DOM nodes. By default, this is jstGetTemplate(). The + * value set here is inherited by clones of this context. + * + * @param {Function} resolver The function used to resolve transclude + * ids into a DOM node of a subtemplate. The DOM node returned by this + * function will be inserted into the template instance being + * processed. Thus, the resolver function must instantiate the + * subtemplate as necessary. + */ +JsExprContext.prototype.setSubTemplateResolver = function(resolver) { + this.resolver_ = resolver; +} + + +/** + * Resolves a sub template from an id. Used to process the transclude + * attribute. If a resolver function was set using + * setSubTemplateResolver(), it will be used, otherwise + * jstGetTemplate(). + * + * @param {String} id The id of the sub template. + * + * @return {Node} The root DOM node of the sub template, for direct + * insertion into the currently processed template instance. + */ +JsExprContext.prototype.getSubTemplate = function(id) { + return (this.resolver_ || jstGetTemplate).call(this, id); +} + + +/** + * HTML template processor. Data values are bound to HTML templates + * using the attributes transclude, jsselect, jsdisplay, jscontent, + * jsvalues. The template is modifed in place. The values of those + * attributes are JavaScript expressions that are evaluated in the + * context of the data object fragment. + * + * @param {JsExprContext} context Context created from the input data + * object. + * + * @param {Element} template DOM node of the template. This will be + * processed in place. After processing, it will still be a valid + * template that, if processed again with the same data, will remain + * unchanged. + */ +function jstProcess(context, template) { + var processor = new JstProcessor(); + processor.run_([ processor, processor.jstProcess_, context, template ]); +} + + +/** + * Internal class used by jstemplates to maintain context. + * NOTE: This is necessary to process deep templates in Safari + * which has a relatively shallow stack. + * @class + */ +function JstProcessor() { +} + + +/** + * Runs the state machine, beginning with function "start". + * + * @param {Array} start The first function to run, in the form + * [object, method, args ...] + */ +JstProcessor.prototype.run_ = function(start) { + var me = this; + + me.queue_ = [ start ]; + while (jsLength(me.queue_)) { + var f = me.queue_.shift(); + f[1].apply(f[0], f.slice(2)); + } +} + + +/** + * Appends a function to be called later. + * Analogous to calling that function on a subsequent line, or a subsequent + * iteration of a loop. + * + * @param {Array} f A function in the form [object, method, args ...] + */ +JstProcessor.prototype.enqueue_ = function(f) { + this.queue_.push(f); +} + + +/** + * Implements internals of jstProcess. + * + * @param {JsExprContext} context + * + * @param {Element} template + */ +JstProcessor.prototype.jstProcess_ = function(context, template) { + var me = this; + + var transclude = domGetAttribute(template, ATT_transclude); + if (transclude) { + var tr = context.getSubTemplate(transclude); + if (tr) { + domReplaceChild(tr, template); + me.enqueue_([ me, me.jstProcess_, context, tr ]); + } else { + domRemoveNode(template); + } + return; + } + + var select = domGetAttribute(template, ATT_select); + if (select) { + me.jstSelect_(context, template, select); + return; + } + + var display = domGetAttribute(template, ATT_display); + if (display) { + if (!context.jseval(display, template)) { + displayNone(template); + return; + } + + displayDefault(template); + } + + + var values = domGetAttribute(template, ATT_values); + if (values) { + me.jstValues_(context, template, values); + } + + var expressions = domGetAttribute(template, ATT_eval); + if (expressions) { + foreach(expressions.split(/\s*;\s*/), function(expression) { + expression = stringTrim(expression); + if (jsLength(expression)) { + context.jseval(expression, template); + } + }); + } + + var content = domGetAttribute(template, ATT_content); + if (content) { + me.jstContent_(context, template, content); + + } else { + var childnodes = []; + for (var i = 0; i < jsLength(template.childNodes); ++i) { + if (template.childNodes[i].nodeType == DOM_ELEMENT_NODE) { + me.enqueue_( + [ me, me.jstProcess_, context, template.childNodes[i] ]); + } + } + } +} + + +/** + * Implements the jsselect attribute: evalutes the value of the + * jsselect attribute in the current context, with the current + * variable bindings (see JsExprContext.jseval()). If the value is an + * array, the current template node is multiplied once for every + * element in the array, with the array element being the context + * object. If the array is empty, or the value is undefined, then the + * current template node is dropped. If the value is not an array, + * then it is just made the context object. + * + * @param {JsExprContext} context The current evaluation context. + * + * @param {Element} template The currently processed node of the template. + * + * @param {String} select The javascript expression to evaluate. + * + * @param {Function} process The function to continue processing with. + */ +JstProcessor.prototype.jstSelect_ = function(context, template, select) { + var me = this; + + var value = context.jseval(select, template); + domRemoveAttribute(template, ATT_select); + + var instance = domGetAttribute(template, ATT_instance); + var instance_last = false; + if (instance) { + if (instance.charAt(0) == '*') { + instance = parseInt10(instance.substr(1)); + instance_last = true; + } else { + instance = parseInt10(instance); + } + } + + var multiple = (value !== null && + typeof value == 'object' && + typeof value.length == 'number'); + var multiple_empty = (multiple && value.length == 0); + + if (multiple) { + if (multiple_empty) { + if (!instance) { + domSetAttribute(template, ATT_select, select); + domSetAttribute(template, ATT_instance, '*0'); + displayNone(template); + } else { + domRemoveNode(template); + } + + } else { + displayDefault(template); + if (instance === null || instance === "" || instance === undefined || + (instance_last && instance < jsLength(value) - 1)) { + var templatenodes = []; + var instances_start = instance || 0; + for (var i = instances_start + 1; i < jsLength(value); ++i) { + var node = domCloneNode(template); + templatenodes.push(node); + domInsertBefore(node, template); + } + templatenodes.push(template); + + for (var i = 0; i < jsLength(templatenodes); ++i) { + var ii = i + instances_start; + var v = value[ii]; + var t = templatenodes[i]; + + me.enqueue_([ me, me.jstProcess_, context.clone(v, ii), t ]); + var instanceStr = (ii == jsLength(value) - 1 ? '*' : '') + ii; + me.enqueue_( + [ null, postProcessMultiple_, t, select, instanceStr ]); + } + + } else if (instance < jsLength(value)) { + var v = value[instance]; + + me.enqueue_( + [me, me.jstProcess_, context.clone(v, instance), template]); + var instanceStr = (instance == jsLength(value) - 1 ? '*' : '') + + instance; + me.enqueue_( + [ null, postProcessMultiple_, template, select, instanceStr ]); + } else { + domRemoveNode(template); + } + } + } else { + if (value == null) { + domSetAttribute(template, ATT_select, select); + displayNone(template); + } else { + me.enqueue_( + [ me, me.jstProcess_, context.clone(value, 0), template ]); + me.enqueue_( + [ null, postProcessSingle_, template, select ]); + } + } +} + + +/** + * Sets ATT_select and ATT_instance following recursion to jstProcess. + * + * @param {Element} template The template + * + * @param {String} select The jsselect string + * + * @param {String} instanceStr The new value for the jsinstance attribute + */ +function postProcessMultiple_(template, select, instanceStr) { + domSetAttribute(template, ATT_select, select); + domSetAttribute(template, ATT_instance, instanceStr); +} + + +/** + * Sets ATT_select and makes the element visible following recursion to + * jstProcess. + * + * @param {Element} template The template + * + * @param {String} select The jsselect string + */ +function postProcessSingle_(template, select) { + domSetAttribute(template, ATT_select, select); + displayDefault(template); +} + + +/** + * Implements the jsvalues attribute: evaluates each of the values and + * assigns them to variables in the current context (if the name + * starts with '$', javascript properties of the current template node + * (if the name starts with '.'), or DOM attributes of the current + * template node (otherwise). Since DOM attribute values are always + * strings, the value is coerced to string in the latter case, + * otherwise it's the uncoerced javascript value. + * + * @param {JsExprContext} context Current evaluation context. + * + * @param {Element} template Currently processed template node. + * + * @param {String} valuesStr Value of the jsvalues attribute to be + * processed. + */ +JstProcessor.prototype.jstValues_ = function(context, template, valuesStr) { + var values = valuesStr.split(/\s*;\s*/); + for (var i = 0; i < jsLength(values); ++i) { + var colon = values[i].indexOf(':'); + if (colon < 0) { + continue; + } + var label = stringTrim(values[i].substr(0, colon)); + var value = context.jseval(values[i].substr(colon + 1), template); + + if (label.charAt(0) == '$') { + context.setVariable(label, value); + + } else if (label.charAt(0) == '.') { + var nameSpaceLabel = label.substr(1).split('.'); + var nameSpaceObject = template; + var nameSpaceDepth = jsLength(nameSpaceLabel); + for (var j = 0, J = nameSpaceDepth - 1; j < J; ++j) { + var jLabel = nameSpaceLabel[j]; + if (!nameSpaceObject[jLabel]) { + nameSpaceObject[jLabel] = {}; + } + nameSpaceObject = nameSpaceObject[jLabel]; + } + nameSpaceObject[nameSpaceLabel[nameSpaceDepth - 1]] = value; + } else if (label) { + if (typeof value == 'boolean') { + if (value) { + domSetAttribute(template, label, label); + } else { + domRemoveAttribute(template, label); + } + } else { + domSetAttribute(template, label, '' + value); + } + } + } +} + + +/** + * Implements the jscontent attribute. Evalutes the expression in + * jscontent in the current context and with the current variables, + * and assigns its string value to the content of the current template + * node. + * + * @param {JsExprContext} context Current evaluation context. + * + * @param {Element} template Currently processed template node. + * + * @param {String} content Value of the jscontent attribute to be + * processed. + */ +JstProcessor.prototype.jstContent_ = function(context, template, content) { + var value = '' + context.jseval(content, template); + if (template.innerHTML == value) { + return; + } + while (template.firstChild) { + domRemoveNode(template.firstChild); + } + var t = domCreateTextNode(ownerDocument(template), value); + domAppendChild(template, t); +} + + +/** + * Helps to implement the transclude attribute, and is the initial + * call to get hold of a template from its ID. + * + * @param {String} name The ID of the HTML element used as template. + * + * @returns {Element} The DOM node of the template. (Only element + * nodes can be found by ID, hence it's a Element.) + */ +function jstGetTemplate(name) { + var section = domGetElementById(document, name); + if (section) { + var ret = domCloneNode(section); + domRemoveAttribute(ret, 'id'); + return ret; + } else { + return null; + } +} + +window['jstGetTemplate'] = jstGetTemplate; +window['jstProcess'] = jstProcess; +window['JsExprContext'] = JsExprContext; diff --git a/chrome/common/extensions/docs/examples/api/tabs/inspector/manifest.json b/chrome/common/extensions/docs/examples/api/tabs/inspector/manifest.json new file mode 100644 index 0000000..1cf3576 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/inspector/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Tab Inspector", + "description": "Utility for working with the extension tabs api", + "version": "0.1", + "permissions": ["tabs"], + "background_page": "background.html", + "browser_action": { + "default_title": "show tab inspector" + } +} diff --git a/chrome/common/extensions/docs/examples/api/tabs/inspector/tabs_api.html b/chrome/common/extensions/docs/examples/api/tabs/inspector/tabs_api.html new file mode 100644 index 0000000..e8047ab --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/inspector/tabs_api.html @@ -0,0 +1,388 @@ +<html> +<head> +<script src="jstemplate_compiled.js" type="text/javascript"></script> +<script> + +tabs = {}; +tabIds = []; + +focusedWindowId = undefined; +currentWindowId = undefined; + +function bootStrap() { + chrome.windows.getCurrent(function(currentWindow) { + currentWindowId = currentWindow.id; + chrome.windows.getLastFocused(function(focusedWindow) { + focusedWindowId = focusedWindow.id; + loadWindowList(); + }); + }); +} + +function isInt(i) { + return (typeof i == "number") && !(i % 1) && !isNaN(i); +} + +function loadWindowList() { + chrome.windows.getAll({ populate: true }, function(windowList) { + tabs = {}; + tabIds = []; + for (var i = 0; i < windowList.length; i++) { + windowList[i].current = (windowList[i].id == currentWindowId); + windowList[i].focused = (windowList[i].id == focusedWindowId); + + for (var j = 0; j < windowList[i].tabs.length; j++) { + tabIds[tabIds.length] = windowList[i].tabs[j].id; + tabs[windowList[i].tabs[j].id] = windowList[i].tabs[j]; + } + } + + var input = new JsExprContext(windowList); + var output = document.getElementById('windowList'); + jstProcess(input, output); + }); +} + +function updateTabData(id) { + var retval = { + url: document.getElementById('url_' + id).value, + selected: document.getElementById('selected_' + id).value ? true : false + } + + return retval; +} + +function updateTab(id){ + try { + chrome.tabs.update(id, updateTabData(id)); + } catch (e) { + alert(e); + } +} + +function moveTabData(id) { + return { + 'index': parseInt(document.getElementById('index_' + id).value), + 'windowId': parseInt(document.getElementById('windowId_' + id).value) + } +} +function moveTab(id) { + try { + chrome.tabs.move(id, moveTabData(id)); + } catch (e) { + alert(e); + } +} + +function createTabData(id) { + return { + 'index': parseInt(document.getElementById('index_' + id).value), + 'windowId': parseInt(document.getElementById('windowId_' + id).value), + 'index': parseInt(document.getElementById('index_' + id).value), + 'url': document.getElementById('url_' + id).value, + 'selected': document.getElementById('selected_' + id).value ? true : false + } +} + +function createTab() { + var args = createTabData('new') + + if (!isInt(args.windowId)) + delete args.windowId; + if (!isInt(args.index)) + delete args.index; + + try { + chrome.tabs.create(args); + } catch (e) { + alert(e); + } +} + +function updateAll() { + try { + for (var i = 0; i < tabIds.length; i++) { + chrome.tabs.update(tabIds[i], updateTabData(tabIds[i])); + } + } catch(e) { + alert(e); + } +} + +function moveAll() { + appendToLog('moving all'); + try { + for (var i = 0; i < tabIds.length; i++) { + chrome.tabs.move(tabIds[i], moveTabData(tabIds[i])); + } + } catch(e) { + alert(e); + } +} + +function removeTab(tabId) { + try { + chrome.tabs.remove(tabId, function() { + appendToLog('tab: ' + tabId + ' removed.'); + }); + } catch (e) { + alert(e); + } +} + +function appendToLog(logLine) { + var log = document.getElementById('log'); + log.innerHTML = '<div> > ' + logLine + '</div>' + log.innerHTML; +} + +function clearLog() { + document.getElementById('log').innerHTML = ''; +} + +chrome.windows.onCreated.addListener(function(createInfo) { + appendToLog('windows.onCreated -- window: ' + createInfo.id); + loadWindowList(); +}); + +chrome.windows.onFocusChanged.addListener(function(windowId) { + focusedWindowId = windowId; + appendToLog('windows.onFocusChanged -- window: ' + windowId); + loadWindowList(); +}); + +chrome.windows.onRemoved.addListener(function(windowId) { + appendToLog('windows.onRemoved -- window: ' + windowId); + loadWindowList(); +}); + +chrome.tabs.onCreated.addListener(function(tab) { + appendToLog('tabs.onCreated -- window: ' + tab.windowId + ' tab: ' + tab.id + ' title: ' + tab.title + ' index ' + tab.index + ' url ' + tab.url); + loadWindowList(); +}); + +chrome.tabs.onAttached.addListener(function(tabId, props) { + appendToLog('tabs.onAttached -- window: ' + props.newWindowId + ' tab: ' + tabId + ' index ' + props.newPosition); + loadWindowList(); +}); + +chrome.tabs.onMoved.addListener(function(tabId, props) { + appendToLog('tabs.onMoved -- window: ' + props.windowId + ' tab: ' + tabId + ' from ' + props.fromIndex + ' to ' + props.toIndex); + loadWindowList(); +}); + +function refreshTab(tabId) { + chrome.tabs.get(tabId, function(tab) { + var input = new JsExprContext(tab); + var output = document.getElementById('tab_' + tab.id); + jstProcess(input, output); + appendToLog('tab refreshed -- tabId: ' + tab.id + ' url: ' + tab.url); + }); +} + +chrome.tabs.onUpdated.addListener(function(tabId, props) { + appendToLog('tabs.onUpdated -- tab: ' + tabId + ' status ' + props.status + ' url ' + props.url); + refreshTab(tabId); +}); + +chrome.tabs.onDetached.addListener(function(tabId, props) { + appendToLog('tabs.onDetached -- window: ' + props.oldWindowId + ' tab: ' + tabId + ' index ' + props.oldPosition); + loadWindowList(); +}); + +chrome.tabs.onSelectionChanged.addListener(function(tabId, props) { + appendToLog('tabs.onSelectionChanged -- window: ' + props.windowId + ' tab: ' + tabId); + loadWindowList(); +}); + +chrome.tabs.onRemoved.addListener(function(tabId) { + appendToLog('tabs.onRemoved -- tab: ' + tabId); + loadWindowList(); +}); + +function createWindow() { + var args = { + 'left': parseInt(document.getElementById('new_window_left').value), + 'top': parseInt(document.getElementById('new_window_top').value), + 'width': parseInt(document.getElementById('new_window_width').value), + 'height': parseInt(document.getElementById('new_window_height').value), + 'url': document.getElementById('new_window_url').value + } + + if (!isInt(args.left)) + delete args.left; + if (!isInt(args.top)) + delete args.top; + if (!isInt(args.width)) + delete args.width; + if (!isInt(args.height)) + delete args.height; + if (!args.url) + delete args.url; + + try { + chrome.windows.create(args); + } catch(e) { + alert(e); + } +} + +function refreshWindow(windowId) { + chrome.windows.get(windowId, function(window) { + chrome.tabs.getAllInWindow(window.id, function(tabList) { + window.tabs = tabList; + var input = new JsExprContext(window); + var output = document.getElementById('window_' + window.id); + jstProcess(input, output); + appendToLog('window refreshed -- windowId: ' + window.id + ' tab count:' + window.tabs.length); + }); + }); +} + +function updateWindowData(id) { + var retval = { + left: parseInt(document.getElementById('left_' + id).value), + top: parseInt(document.getElementById('top_' + id).value), + width: parseInt(document.getElementById('width_' + id).value), + height: parseInt(document.getElementById('height_' + id).value) + } + if (!isInt(retval.left)) + delete retval.left; + if (!isInt(retval.top)) + delete retval.top; + if (!isInt(retval.width)) + delete retval.width; + if (!isInt(retval.height)) + delete retval.height; + + return retval; +} + +function updateWindow(id){ + try { + chrome.windows.update(id, updateWindowData(id)); + } catch (e) { + alert(e); + } +} + +function removeWindow(windowId) { + try { + chrome.windows.remove(windowId, function() { + appendToLog('window: ' + windowId + ' removed.'); + }); + } catch (e) { + alert(e); + } +} + +function refreshSelectedTab(windowId) { + chrome.tabs.getSelected(windowId, function(tab) { + var input = new JsExprContext(tab); + var output = document.getElementById('tab_' + tab.id); + jstProcess(input, output); + appendToLog('selected tab refreshed -- tabId: ' + tab.id + ' url:' + tab.url); + }); +} + +</script> +</head> + <body onload="bootStrap();"> + <div id="windowList"> + <div style="background-color: #AAEEEE; margin: 4px; padding: 8px; margin: 20px" jsselect="$this" + jsvalues="id:'window_' + id"> + <div style="font-style: italic; width: 80px; display: inline-block"> + Window: <span jscontent="id"></span> + </div> + <div style="display: inline-block"> + left: <input style="width: 60px" type="text" jsvalues="value:$this.left;id:'left_' + id" /> + top: <input style="width: 60px" type="text" jsvalues="value:$this.top;id:'top_' + id" /> + width: <input style="width: 60px" type="text" jsvalues="value:$this.width;id:'width_' + id" /> + height: <input style="width: 60px" type="text" jsvalues="value:$this.height;id:'height_' + id" /> + <input type="checkbox" jsvalues="checked:focused; id:'focused_' + id" /> Focused + <input type="checkbox" jsvalues="checked:current; id:'current_' + id" /> Current + <button onclick="refreshWindow(this.jstdata);" jsvalues=".jstdata:id">Refresh</button> + </div> + <div id="tabList"> + <div jsselect="tabs"> + <div style="background-color: #EEEEEE; margin: 8px; padding: 4px" jsvalues="id:'tab_' + id"> + <div style="margin: 8px"> + <div style="font-style: italic; width: 80px; display: inline-block" jscontent="'TabId: ' + id"></div> + <div style="width: 300px; display: inline-block"> + index: <input style="width: 20px" type="text" jsvalues="value:$this.index;id:'index_' + id" /> + windowId: <input style="width: 20px" type="text" jsvalues="value:windowId;id:'windowId_' + id" /> + <button onclick="moveTab(this.jstdata);" jsvalues=".jstdata:id">Move</button> + <button onclick="refreshTab(this.jstdata);" jsvalues=".jstdata:id">Refresh</button> + </div> + </div> + <div style="margin: 8px"> + <div> + <div style="width: 40px; display:inline-block">title:</div> + <input style="width: 90%" type="text" jsvalues="value:title;id:'title_' + id" /> + </div> + <div> + <div style="width: 40px; display:inline-block">url:</div> + <input style="width: 90%" type="text" jsvalues="value:url;id:'url_' + id" /> + </div> + <div><input type="checkbox" jsvalues="checked:selected; id:'selected_' + id" /> Selected</div> + </div> + <button onclick="updateTab(this.jstdata)" jsvalues=".jstdata:id">Update Tab</button> + <button onclick="removeTab(this.jstdata);" jsvalues=".jstdata:id">Close Tab</button> + </div> + </div> + </div> + <button onclick="updateWindow(this.jstdata);" jsvalues=".jstdata:id">Update Window</button> + <button onclick="removeWindow(this.jstdata);" jsvalues=".jstdata:id">Close Window</button> + <button onclick="refreshSelectedTab(this.jstdata);" jsvalues=".jstdata:id">Refresh Selected Tab</button> + </div> + </div> + <div style="background-color: #EEEEBB; margin: 20px; padding: 8px"> + <h3 style="text-align: center; margin: 8px"> Create Window</h3> + <div style="margin: 8px"> + <div style="width: 300px; display: inline-block"> + left: <input style="width: 20px" type="text" id="new_window_left" /> + top: <input style="width: 20px" type="text" id="new_window_top" /> + width: <input style="width: 20px" type="text" id="new_window_width" /> + height: <input style="width: 20px" type="text" id="new_window_height" /> + </div> + </div> + <div style="margin: 8px"> + <div> + <div style="width: 40px; display:inline-block">url:</div> + <input style="width: 90%" type="text" id="new_window_url" /> + </div> + </div> + <button onclick="createWindow();">Create</button> + </div> + <div style="background-color: #EEEEAA; margin: 20px; padding: 8px"> + <h3 style="text-align: center; margin: 8px"> Create Tab</h3> + <div style="margin: 8px"> + <div style="width: 300px; display: inline-block"> + index: <input style="width: 20px" type="text" id="index_new" /> + windowId: <input style="width: 20px" type="text" id="windowId_new" /> + <button onclick="moveTab(this.jstdata);" jsvalues=".jstdata:id">Move</button> + </div> + </div> + <div style="margin: 8px"> + <div> + <div style="width: 40px; display:inline-block">title:</div> + <input style="width: 90%" type="text" id="title_new" /> + </div> + <div> + <div style="width: 40px; display:inline-block">url:</div> + <input style="width: 90%" type="text" id="url_new" /> + </div> + <div><input type="checkbox" id="selected_new" /> Selected</div> + </div> + <button onclick="createTab();">Create</button> + </div> + <div style="margin: 20px;"> + <button onclick="loadWindowList();">Refresh</button> + <button onclick="updateAll();">Update All</button> + <button onclick="moveAll();">Move All</button> + <button onclick="clearLog();">-->Clear Log</button> + <button onclick="chrome.windows.create();">New Window</button> + </div> + <div id="log" style="background-color: #EEAAEE; margin: 20px; padding: 8px"> + </div> + </body> +</html>
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/api/tabs/screenshot/background.html b/chrome/common/extensions/docs/examples/api/tabs/screenshot/background.html new file mode 100644 index 0000000..2955688 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/screenshot/background.html @@ -0,0 +1,10 @@ + +<html> +<head> +<script src='screenshot.js' type='text/javascript' /> +<script> +</script> +</head> +<body> +</body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/tabs/screenshot/camera.png b/chrome/common/extensions/docs/examples/api/tabs/screenshot/camera.png Binary files differnew file mode 100755 index 0000000..be26c39 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/screenshot/camera.png diff --git a/chrome/common/extensions/docs/examples/api/tabs/screenshot/manifest.json b/chrome/common/extensions/docs/examples/api/tabs/screenshot/manifest.json new file mode 100755 index 0000000..37fdbf0 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/screenshot/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Test Screenshot Extension", + "version": "1.0", + "description": "Demonstrate screenshot functionality in the chrome.tabs api.", + "background_page": "background.html", + "browser_action": { + "default_icon": "camera.png", + "default_title": "Take a screen shot!" + }, + "permissions": ["tabs"] +} diff --git a/chrome/common/extensions/docs/examples/api/tabs/screenshot/screenshot.html b/chrome/common/extensions/docs/examples/api/tabs/screenshot/screenshot.html new file mode 100644 index 0000000..408d13d --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/screenshot/screenshot.html @@ -0,0 +1,14 @@ +<html> +<script> +function setScreenshotUrl(url) { + document.getElementById('target').src = url; +} +</script> +<body> + Image here: + <p> + <img id="target" src="white.png" width="640" height="480"> + <p> + End image + </body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/tabs/screenshot/screenshot.js b/chrome/common/extensions/docs/examples/api/tabs/screenshot/screenshot.js new file mode 100755 index 0000000..5d47a99 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/screenshot/screenshot.js @@ -0,0 +1,47 @@ +function takeScreenshot() { + chrome.tabs.captureVisibleTab(null, function(img) { + var screenshotUrl = img; + var viewTabUrl = chrome.extension.getURL('screenshot.html'); + + chrome.tabs.create({url: viewTabUrl}, function(tab) { + var targetId = tab.id; + + var addSnapshotImageToTab = function(tabId, changedProps) { + // We are waiting for the tab we opened to finish loading. + // Check that the the tab's id matches the tab we opened, + // and that the tab is done loading. + if (tabId != targetId || changedProps.status != "complete") + return; + + // Passing the above test means this is the event we were waiting for. + // There is nothing we need to do for future onUpdated events, so we + // use removeListner to stop geting called when onUpdated events fire. + chrome.tabs.onUpdated.removeListener(addSnapshotImageToTab); + + // Look through all views to find the window which will display + // the screenshot. + var views = chrome.extension.getViews(); + for (var i = 0; i < views.length; i++) { + var view = views[i]; + // If more than one screen shot tab is opened, we need to + // ensure that we do not change an existing screen shot image. + // view.imageAlreadySet is set to true when an image is set for + // the first time. We never change an image in a window with + // this flag set. + if (view.location.href == viewTabUrl && !view.imageAlreadySet) { + view.setScreenshotUrl(screenshotUrl); + view.imageAlreadySet = true; + break; + } + } + }; + chrome.tabs.onUpdated.addListener(addSnapshotImageToTab); + + }); + }); +} + +// Listen for a click on the camera icon. On that click, take a screenshot. +chrome.browserAction.onClicked.addListener(function(tab) { + takeScreenshot(); +}); diff --git a/chrome/common/extensions/docs/examples/api/tabs/screenshot/white.png b/chrome/common/extensions/docs/examples/api/tabs/screenshot/white.png Binary files differnew file mode 100644 index 0000000..06d8aca --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/screenshot/white.png diff --git a/chrome/common/extensions/docs/examples/extensions/buildbot/bg.html b/chrome/common/extensions/docs/examples/extensions/buildbot/bg.html new file mode 100644 index 0000000..1324fb1 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/buildbot/bg.html @@ -0,0 +1,58 @@ +<script> +var statusURL = "http://chromium-status.appspot.com/current"; + +function updateStatus(text) { + // Page outputs a line like this: + // <div class="Notice">Tree is open (issue -> person) </div> + // Although what's in the <div> is arbitrary and typed by a person. + var results = (new RegExp('"Notice"[^>]*>(.*)<', 'g')).exec(text); + if (!results || results.index < 0) { + throw new Error("couldn't find status div in " + text); + } + var status = results[1]; + chrome.browserAction.setTitle({title:status}); + var open = /open/i; + if (open.exec(status)) { + //chrome.browserAction.setBadgeText("\u263A"); + chrome.browserAction.setBadgeText({text:"\u2022"}); + chrome.browserAction.setBadgeBackgroundColor({color:[0,255,0,255]}); + } else { + //chrome.browserAction.setBadgeText("\u2639"); + chrome.browserAction.setBadgeText({text:"\u00D7"}); + chrome.browserAction.setBadgeBackgroundColor({color:[255,0,0,255]}); + } +} + +function requestStatus() { + requestURL(statusURL, updateStatus); + setTimeout(requestStatus, 30000); +} + +function requestURL(url, callback) { + console.log("requestURL: " + url); + var xhr = new XMLHttpRequest(); + try { + xhr.onreadystatechange = function(state) { + if (xhr.readyState == 4) { + var text = xhr.responseText; + //console.log(text); + callback(text); + } + } + + xhr.onerror = function(error) { + console.log("xhr error: " + JSON.stringify(error)); + console.dir(error); + } + + xhr.open("GET", url, true); + xhr.send({}); + } catch(e) { + console.log("exception: " + e); + } +} + +window.onload = function() { + window.setTimeout(requestStatus, 10); +} +</script> diff --git a/chrome/common/extensions/docs/examples/extensions/buildbot/chromium.png b/chrome/common/extensions/docs/examples/extensions/buildbot/chromium.png Binary files differnew file mode 100755 index 0000000..cc9fb60 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/buildbot/chromium.png diff --git a/chrome/common/extensions/docs/examples/extensions/buildbot/icon.png b/chrome/common/extensions/docs/examples/extensions/buildbot/icon.png Binary files differnew file mode 100755 index 0000000..b3c3474 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/buildbot/icon.png diff --git a/chrome/common/extensions/docs/examples/extensions/buildbot/manifest.json b/chrome/common/extensions/docs/examples/extensions/buildbot/manifest.json new file mode 100644 index 0000000..5e0349c --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/buildbot/manifest.json @@ -0,0 +1,17 @@ +{ + "name": "Chromium Buildbot Monitor", + "version": "0.7.1", + "description": "Displays the status of the Chromium buildbot in the toolbar. On hover, expands to give more detail about each failing bot.", + "icons": { "128": "icon.png" }, + "background_page": "bg.html", + "permissions": [ + "http://build.chromium.org/", + "http://chromium-status.appspot.com/", + "http://chrome-buildbot.corp.google.com/" + ], + "browser_action": { + "default_title": "", + "default_icon": "chromium.png", + "popup": "popup.html" + } +} diff --git a/chrome/common/extensions/docs/examples/extensions/buildbot/popup.html b/chrome/common/extensions/docs/examples/extensions/buildbot/popup.html new file mode 100644 index 0000000..d5b913d --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/buildbot/popup.html @@ -0,0 +1,218 @@ +<head> +<script> +var botRoot = "http://build.chromium.org/buildbot"; +//var botRoot = "http://chrome-buildbot.corp.google.com:8010"; +var waterfallURL = botRoot + "/waterfall/bot_status.json?json=1"; +var botList; +var checkinResults; +var bots; +var failures; + +function updateBotList(text) { + var results = (new RegExp('(.*)<\/body>', 'g')).exec(text); + if (!results || results.index < 0) { + console.log("Error: couldn't find bot JSON"); + console.log(text); + return; + } + var data; + try { + data = JSON.parse(results[1]); + } catch (e) { + console.dir(e); + console.log(text); + return; + } + if (!data) { + throw new Error("JSON parse fail: " + results[1]); + } + botList = data[0]; + checkinResults = data[1]; + + failures = botList.filter(function(bot) { + return (bot.color != "success"); + }); + displayFailures(); +} + +function displayFailures() { + var html = ""; + if (failures.length == 0) { + html = "<a href='' onclick='showConsole()' class='open'>" + + "The tree is completely green.</a> (no way!)"; + } else { + html = "<div><a class='closed' href='' onclick='showFailures()'>" + + "failures:</a></div>"; + failures.forEach(function(bot, i) { + html += "<div class='bot " + bot.color + + "' onclick='showBot(" + i + ")'>" + + bot.title + "</div>"; + }); + } + bots.innerHTML = html; +} + +function showURL(url) { + window.open(url); + window.event.stopPropagation(); +} + +function showBot(botIndex) { + var bot = failures[botIndex]; + var url = botRoot + "/waterfall/waterfall?builder=" + bot.name; + showURL(url); +} + +function showConsole() { + var url = botRoot + "/waterfall/console"; + showURL(url); +} + +function showTry() { + var url = botRoot + "/try-server/waterfall"; + showURL(url); +} + +function showFyi() { + var url = botRoot + "/waterfall.fyi/console"; + showURL(url); +} + +function showFailures() { + var url = botRoot + "/waterfall/waterfall?show_events=true&failures_only=true"; + showURL(url); +} + +function requestURL(url, callback) { + console.log("requestURL: " + url); + var xhr = new XMLHttpRequest(); + try { + xhr.onreadystatechange = function(state) { + if (xhr.readyState == 4) { + var text = xhr.responseText; + //console.log(text); + callback(text); + } + } + + xhr.onerror = function(error) { + console.log("xhr error: " + JSON.stringify(error)); + console.dir(error); + } + + xhr.open("GET", url, true); + xhr.send({}); + } catch(e) { + console.log("exception: " + e); + } +} +window.onload = function() { + bots = document.getElementById("bots"); + + // XHR from onload winds up blocking the load, so we put it in a setTimeout. + window.setTimeout(requestURL, 0, waterfallURL, updateBotList); +} + +function toggle_size() { + if (document.body.className == "big") { + document.body.className = "small"; + } else { + document.body.className = "big"; + } +} + +</script> +<style> +body { + font: menu; + overflow: hidden; +} + +#links { + background-color: #efefef; + border: 1px solid #cccccc; + -webkit-border-radius: 5px; + margin-top: 1px; + padding: 3px; + white-space: nowrap; + text-align: center; +} + +a { + text-decoration: underline; + color: #444; +} + +a:hover { + color: black; + cursor: pointer; +} + +body.big .bot { + -webkit-transition: all .5s ease-out; + margin: 20px; +} + +body.small .bot { + -webkit-transition: all .5s ease-out; +} + +.bot { + cursor: pointer; + -webkit-border-radius: 5px; + margin-top: 1px; + padding: 3px; + white-space: nowrap; +} + +.bot:hover { + border: 2px solid black; + padding: 2px; +} + +.open { + color: green; +} + +.closed { + color: red; +} + +.running { + background-color: rgb(255, 252, 108); + border: 1px solid rgb(197, 197, 109); +} + +.notstarted { + border: 1px solid rgb(170, 170, 170); +} + +.failure { + background-color: rgb(233, 128, 128); + border: 1px solid rgb(167, 114, 114); +} + +.warnings { + background-color: rgb(255, 195, 67); + border: 1px solid rgb(194, 157, 70); +} + +.success { + background-color: rgb(143, 223, 95); + border: 1px solid rgb(79, 133, 48); +} + +.exception { + background-color: rgb(224, 176, 255); + border: 1px solid rgb(172, 160, 179); +} +</style> +</head> +<body onclick="toggle_size()"> +<div id="links"> +<a href="" onclick='showConsole()'>console</a> - +<a href="" onclick='showTry()'>try</a> - +<a href="" onclick='showFyi()'>fyi</a> +</div> +<div id="bots">Loading....</div> +</body>
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/extensions/gmail/background.html b/chrome/common/extensions/docs/examples/extensions/gmail/background.html new file mode 100644 index 0000000..eea77b6 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/gmail/background.html @@ -0,0 +1,235 @@ +<html> +<head> +<script> +var animationFrames = 36; +var animationSpeed = 10; // ms +var canvas; +var canvasContext; +var gmail = "http://mail.google.com/"; +var gmailAtomRef = "http://mail.google.com/mail/feed/atom"; +var loggedInImage; +var pollInterval = 1000 * 10; // 10 seconds +var requestTimeout = 1000 * 2; // 5 seconds +var rotation = 0; +var unreadCount = -1; +var loadingAnimation = new LoadingAnimation(); + + +// A "loading" animation displayed while we wait for the first response from +// Gmail. This animates the badge text with a dot that cycles from left to +// right. +function LoadingAnimation() { + this.timerId_ = 0; + this.maxCount_ = 8; // Total number of states in animation + this.current_ = 0; // Current state + this.maxDot_ = 4; // Max number of dots in animation +} + +LoadingAnimation.prototype.paintFrame = function() { + var text = ""; + for (var i = 0; i < this.maxDot_; i++) { + text += (i == this.current_) ? "." : " "; + } + if (this.current_ >= this.maxDot_) + text += ""; + + chrome.browserAction.setBadgeText({text:text}); + this.current_++; + if (this.current_ == this.maxCount_) + this.current_ = 0; +} + +LoadingAnimation.prototype.start = function() { + if (this.timerId_) + return; + + var self = this; + this.timerId_ = window.setInterval(function() { + self.paintFrame(); + }, 100); +} + +LoadingAnimation.prototype.stop = function() { + if (!this.timerId_) + return; + + window.clearInterval(this.timerId_); + this.timerId_ = 0; +} + + +chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) { + if (changeInfo.url && changeInfo.url.indexOf(gmail) == 0) { + console.log("saw gmail! updating..."); + getInboxCount(function(count) { + updateUnreadCount(count); + }); + } +}); + + +function init() { + canvas = document.getElementById('canvas'); + loggedInImage = document.getElementById('logged_in'); + canvasContext = canvas.getContext('2d'); + + chrome.browserAction.setIcon({path: "gmail_logged_in.png"}); + loadingAnimation.start(); + + startRequest(); +} + +function scheduleRequest() { + window.setTimeout(startRequest, pollInterval); +} + +// ajax stuff +function startRequest() { + getInboxCount( + function(count) { + loadingAnimation.stop(); + updateUnreadCount(count); + scheduleRequest(); + }, + function() { + loadingAnimation.stop(); + showLoggedOut(); + scheduleRequest(); + } + ); +} + +function getInboxCount(onSuccess, onError) { + var xhr = new XMLHttpRequest(); + var abortTimerId = window.setTimeout(function() { + xhr.abort(); + onError(); + }, requestTimeout); + + function handleSuccess(count) { + window.clearTimeout(abortTimerId); + if (onSuccess) + onSuccess(count); + } + + function handleError() { + window.clearTimeout(abortTimerId); + if (onError) + onError(); + } + + try { + console.log("request.."); + xhr.onreadystatechange = function(){ + console.log("readystate: " + xhr.readyState); + if (xhr.readyState == 4) { + console.log("responseText: " + xhr.responseText.substring(0, 200) + + "..."); + if (xhr.responseXML) { + var xmlDoc = xhr.responseXML; + var fullCountSet = xmlDoc.evaluate("/gmail:feed/gmail:fullcount", + xmlDoc, gmailNSResolver, XPathResult.ANY_TYPE, null); + var fullCountNode = fullCountSet.iterateNext(); + if (fullCountNode) { + handleSuccess(fullCountNode.textContent); + } else { + console.log("fullcount not found!"); + console.error("Error: feed retrieved, but no <fullcount> node " + + "found"); + handleError(); + } + } else { + console.log("No responseXML!"); + } + } + } + + xhr.onerror = function(error) { + console.log("error"); + console.log(error); + handleError(); + } + + xhr.open("GET", gmailAtomRef, true); + xhr.send(null); + } catch(e) { + console.log("ex: " + e); + console.error("exception: " + e); + handleError(); + } +} + +function gmailNSResolver(prefix) { + if(prefix == 'gmail') { + return 'http://purl.org/atom/ns#'; + } +} + +function updateUnreadCount(count) { + if (unreadCount != count) { + unreadCount = count; + animateFlip(); + } +} + + +function ease(x) { + return (1-Math.sin(Math.PI/2+x*Math.PI))/2; +} + +function animateFlip() { + rotation += 1/animationFrames; + drawIconAtRotation(); + + if (rotation <= 1) { + setTimeout("animateFlip()", animationSpeed); + } else { + rotation = 0; + drawIconAtRotation(); + chrome.browserAction.setBadgeText({ + text: unreadCount != "0" ? unreadCount : "" + }); + chrome.browserAction.setBadgeBackgroundColor({color:[208, 0, 24, 255]}); + } +} + +function showLoggedOut() { + unreadCount = -1; + chrome.browserAction.setIcon({path:"gmail_not_logged_in.png"}); + chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]}); + chrome.browserAction.setBadgeText({text:"?"}); +} + +function drawIconAtRotation() { + canvasContext.save(); + canvasContext.clearRect(0, 0, canvas.width, canvas.height); + canvasContext.translate( + Math.ceil(canvas.width/2), + Math.ceil(canvas.height/2)); + canvasContext.rotate(2*Math.PI*ease(rotation)); + canvasContext.drawImage(loggedInImage, + -Math.ceil(canvas.width/2), + -Math.ceil(canvas.height/2)); + canvasContext.restore(); + + chrome.browserAction.setIcon({imageData:canvasContext.getImageData(0, 0, + canvas.width,canvas.height)}); +} + +function goToInbox() { + chrome.tabs.create({url: gmail}); +} + +// Called when the user clicks on the browser action. +chrome.browserAction.onClicked.addListener(function(tab) { + goToInbox(); +}); + +</script> +</head> +<body onload="init()"> +<img id="logged_in" src="gmail_logged_in.png"> +<canvas id="canvas" width="19" height="19"> +</body> +</html> + diff --git a/chrome/common/extensions/docs/examples/extensions/gmail/gmail_logged_in.png b/chrome/common/extensions/docs/examples/extensions/gmail/gmail_logged_in.png Binary files differnew file mode 100644 index 0000000..8d5e256 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/gmail/gmail_logged_in.png diff --git a/chrome/common/extensions/docs/examples/extensions/gmail/gmail_not_logged_in.png b/chrome/common/extensions/docs/examples/extensions/gmail/gmail_not_logged_in.png Binary files differnew file mode 100644 index 0000000..582d262 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/gmail/gmail_not_logged_in.png diff --git a/chrome/common/extensions/docs/examples/extensions/gmail/icon_128.png b/chrome/common/extensions/docs/examples/extensions/gmail/icon_128.png Binary files differnew file mode 100755 index 0000000..1ddc33b --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/gmail/icon_128.png diff --git a/chrome/common/extensions/docs/examples/extensions/gmail/manifest.json b/chrome/common/extensions/docs/examples/extensions/gmail/manifest.json new file mode 100644 index 0000000..3dc942f --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/gmail/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "Google Mail Checker", + "description": "This extension adds a Google Mail button to the toolbar which displays the number of unread messages in your inbox. You can also click the button to open your inbox.", + "version": "1.0", + "background_page": "background.html", + "permissions": [ + "tabs", "http://*.google.com/", "https://*.google.com/" + ], + "browser_action": { + "default_title": "" + }, + "icons": { + "128": "icon_128.png" + } +} diff --git a/chrome/common/extensions/docs/examples/extensions/mappy/icon.png b/chrome/common/extensions/docs/examples/extensions/mappy/icon.png Binary files differnew file mode 100755 index 0000000..40ebbff --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/mappy/icon.png diff --git a/chrome/common/extensions/docs/examples/extensions/mappy/manifest.json b/chrome/common/extensions/docs/examples/extensions/mappy/manifest.json new file mode 100755 index 0000000..344920f --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/mappy/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "Mappy", + "version": "0.6", + "description": "Finds addresses in the web page you're on and pops up a map window.", + "icons": { "128": "icon.png" }, + "content_scripts": [ + { "matches": ["http://*/*"], "js": ["mappy_content_script.js"] } + ], + "permissions": [ + "tabs", + "http://maps.google.com/*" + ], + "browser_action": { + "name": "Display Map", + "icons": ["marker.png"], + "popup": { "path": "popup.html", "height": 512 } + } +} diff --git a/chrome/common/extensions/docs/examples/extensions/mappy/mappy_content_script.js b/chrome/common/extensions/docs/examples/extensions/mappy/mappy_content_script.js new file mode 100755 index 0000000..7e17af7 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/mappy/mappy_content_script.js @@ -0,0 +1,55 @@ +// find map on demand + +console.log("mappy_content_script.js loaded"); + +var maps_key = "ABQIAAAATfHumDbW3OmRByfquHd3SRTRERdeAiwZ9EeJWta3L_JZVS0bOBRQeZgr4K0xyVKzUdnnuFl8X9PX0w"; + +chrome.extension.onConnect.addListener(function(port) { + //console.log("extension connected"); + port.onMessage.addListener(function(data) { + //console.log("extension sent message"); + findAddress(port); + }); +}); + +var findAddress = function(port) { + var found; + var re = /(\d+\s+[':.,\s\w]*,\s*[A-Za-z]+\s*\d{5}(-\d{4})?)/m; + var node = document.body; + var done = false; + while (!done) { + done = true; + for (var i = 0; i < node.childNodes.length; ++i) { + var child = node.childNodes[i]; + if (child.textContent.match(re)) { + node = child; + found = node; + done = false; + break; + } + } + } + if (found) { + var text = ""; + if (found.childNodes.length) { + for (var i = 0; i < found.childNodes.length; ++i) { + text += found.childNodes[i].textContent + " "; + } + } else { + text = found.textContent; + } + var match = re.exec(text); + if (match && match.length) { + console.log("found: " + match[0]); + var trim = /\s{2,}/g; + var map = match[0].replace(trim, " "); + port.postMessage({message:"map", values:[map]}); + } else { + console.log("found bad " + found.textContent); + console.log("no match in: " + text); + } + } else { + console.log("no match in " + node.textContent); + } +} + diff --git a/chrome/common/extensions/docs/examples/extensions/mappy/marker.png b/chrome/common/extensions/docs/examples/extensions/mappy/marker.png Binary files differnew file mode 100755 index 0000000..aa45f4d --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/mappy/marker.png diff --git a/chrome/common/extensions/docs/examples/extensions/mappy/popup.html b/chrome/common/extensions/docs/examples/extensions/mappy/popup.html new file mode 100644 index 0000000..409b216 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/mappy/popup.html @@ -0,0 +1,51 @@ +<head> +<style> +body { + margin: 0px; + padding: 0px; +} +#map { + width: 512px; + height: 512px; +} +</style> +<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAATfHumDbW3OmRByfquHd3SRTRERdeAiwZ9EeJWta3L_JZVS0bOBRQeZgr4K0xyVKzUdnnuFl8X9PX0w&sensor=false" + type="text/javascript"></script> +<script> +var maps_key = "ABQIAAAATfHumDbW3OmRByfquHd3SRTRERdeAiwZ9EeJWta3L_JZVS0bOBRQeZgr4K0xyVKzUdnnuFl8X9PX0w"; + +function gclient_geocode(address) { + var geocoder = new GClientGeocoder(); + geocoder.getLatLng(address, function(point) { + if (!point) { + console.log(address + " not found"); + } else { + var latlng = point.toUrlValue(); + var url = "http://maps.google.com/staticmap?center=" + latlng + + "&markers=" + latlng + "&zoom=14" + + "&size=512x512&sensor=false&key=" + maps_key; + var map = document.getElementById("map"); + map.src = url; + } + }); +} + +function map() { + chrome.tabs.getSelected(null, function(tab) { + var port = chrome.tabs.connect(tab.id); + if (!port) { + console.log("no port"); + } else { + port.onMessage.addListener(function(data) { + var address = data.values[0]; + gclient_geocode(address); + }); + port.postMessage({message: "hello tab: " + tab.id}); + } + }); +}; +</script> +</head> +<body onload="map()"> +<img id="map" onclick="window.close()"> +</body> diff --git a/chrome/common/extensions/docs/examples/tutorials/debugging/hello_world.html b/chrome/common/extensions/docs/examples/tutorials/debugging/hello_world.html new file mode 100644 index 0000000..5cf5fab --- /dev/null +++ b/chrome/common/extensions/docs/examples/tutorials/debugging/hello_world.html @@ -0,0 +1,45 @@ +<style> +img { + margin:5px; + border:2px solid black; + vertical-align:middle; + width:75px; + height:75px; +} +</style> + +<script> +var req = new XMLHttpRequest(); +req.open( + "GET", + "http://api.flickr.com/services/rest/?" + + "method=flickr.photos.search&" + + "api_key=90485e931f687a9b9c2a66bf58a3861a&" + + "text=hello%20world&" + + "safe_search=1&" + // 1 is "safe" + "content_type=1&" + // 1 is "photos only" + "sort=relevance&" + // another good one is "interestingness-desc" + "per_page=95", + true); +req.onload = showPhotos; +req.send(null); + +function showPhotos() { + var photos = req.responseXML.getElementsByTagName("photo"); + + for (var i = 0, photo; photo = photos[i]; i++) { + var img = document.createElement("image"); + img.src = constructImageURL(photo); + document.body.appendChild(img); + } +} + +// See: http://www.flickr.com/services/api/misc.urls.html +function constructImageURL(photo) { + return "http://farm" + photo.getAttribute("farm") + + ".static.flickr.com/" + photo.getAttribute("server") + + "/" + photo.getAttribute("id") + + "_" + photo.getAttribute("secret") + + "_s.jpg"; +} +</script> diff --git a/chrome/common/extensions/docs/examples/tutorials/debugging/manifest.json b/chrome/common/extensions/docs/examples/tutorials/debugging/manifest.json new file mode 100644 index 0000000..3170bf4 --- /dev/null +++ b/chrome/common/extensions/docs/examples/tutorials/debugging/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "My First Extension", + "version": "1.0", + "description": "The first extension that I made.", + "toolstrips": [ + "my_toolstrip.html" + ], + "permissions": [ + "http://api.flickr.com/" + ] +} diff --git a/chrome/common/extensions/docs/examples/tutorials/debugging/my_toolstrip.html b/chrome/common/extensions/docs/examples/tutorials/debugging/my_toolstrip.html new file mode 100644 index 0000000..a6b70d5 --- /dev/null +++ b/chrome/common/extensions/docs/examples/tutorials/debugging/my_toolstrip.html @@ -0,0 +1,3 @@ +<div class="toolstrip-button" onclick="window.open('hello_world.html')"> + <span>Hello, World!</span> +</div> diff --git a/chrome/common/extensions/docs/samples.html b/chrome/common/extensions/docs/samples.html index ccb422c..4fdaebc 100644 --- a/chrome/common/extensions/docs/samples.html +++ b/chrome/common/extensions/docs/samples.html @@ -223,7 +223,7 @@ </p><p><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dmihcahmgecmbnbcchbopgniflfhgnkff%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0"><img src="images/google-mail-checker-capture.png" width="243" height="170" style="margin-bottom:0.5em"></a><br> <b><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dmihcahmgecmbnbcchbopgniflfhgnkff%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0">Install</a> -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/test/data/extensions/samples/gmail_browser_action/">Source code</a></b> +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/gmail/">Source code</a></b> <br><br> </p><p>Features used: @@ -244,7 +244,7 @@ </p><p><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dafmppjmdopaajlhgcddfhfhfgincjeih%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0"><img src="images/buildbot-capture.png" style="margin-bottom:0.5em" width="336" height="288"></a><br> <b><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dafmppjmdopaajlhgcddfhfhfgincjeih%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0">Install</a> -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/test/data/extensions/samples/buildbot/">Source code</a></b> +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/buildbot/">Source code</a></b> <br><br> @@ -267,7 +267,7 @@ </p><p><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dnlbjncdgjeocebhnmkbbbdekmmmcbfjd%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0"><img src="images/subscribe-cap2.png" style="margin-bottom:0.5em" width="566" height="327"></a><br> <b><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dnlbjncdgjeocebhnmkbbbdekmmmcbfjd%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0">Install</a> -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/test/data/extensions/samples/subscribe_page_action/">Source code</a></b> +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/test/data/extensions/subscribe_page_action/">Source code</a></b> <br><br> </p><p>Features used: diff --git a/chrome/common/extensions/docs/static/samples.html b/chrome/common/extensions/docs/static/samples.html index 3a48d5d..92a8faa 100644 --- a/chrome/common/extensions/docs/static/samples.html +++ b/chrome/common/extensions/docs/static/samples.html @@ -8,7 +8,7 @@ <p><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dmihcahmgecmbnbcchbopgniflfhgnkff%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0"><img src="images/google-mail-checker-capture.png" width="243" height="170" style="margin-bottom:0.5em"></a><br> <b><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dmihcahmgecmbnbcchbopgniflfhgnkff%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0">Install</a> -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/test/data/extensions/samples/gmail_browser_action/">Source code</a></b> +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/gmail/">Source code</a></b> <br><br> <p>Features used: @@ -29,7 +29,7 @@ <p><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dafmppjmdopaajlhgcddfhfhfgincjeih%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0"><img src="images/buildbot-capture.png" style="margin-bottom:0.5em" width="336" height="288"></a><br> <b><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dafmppjmdopaajlhgcddfhfhfgincjeih%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0">Install</a> -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/test/data/extensions/samples/buildbot/">Source code</a></b> +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/buildbot/">Source code</a></b> <br><br> @@ -52,7 +52,7 @@ <p><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dnlbjncdgjeocebhnmkbbbdekmmmcbfjd%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0"><img src="images/subscribe-cap2.png" style="margin-bottom:0.5em" width="566" height="327"></a><br> <b><a href="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dnlbjncdgjeocebhnmkbbbdekmmmcbfjd%26uc%26lang%3Den-US&prod=chrome&prodversion=4.0.238.0">Install</a> -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/test/data/extensions/samples/subscribe_page_action/">Source code</a></b> +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/test/data/extensions/subscribe_page_action/">Source code</a></b> <br><br> <p>Features used: |